This commit is contained in:
melekhin
2026-06-11 15:42:01 +07:00
parent 343996ef46
commit 790a5f8e10
35 changed files with 5128 additions and 3595 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -577,6 +577,498 @@ public readonly partial record struct {typeName} : IMensuraUnit<{typeName}>, IEq
public static {typeName}[] operator /({typeName}? T1, UInt128[] T2) => T1.ProtectedU().Div(T2.ToDouble()); public static {typeName}[] operator /({typeName}? T1, UInt128[] T2) => T1.ProtectedU().Div(T2.ToDouble());
public static {typeName}?[] operator /({typeName}? T1, UInt128?[] T2) => T1.ProtectedU().Div(T2.ToDouble()); public static {typeName}?[] operator /({typeName}? T1, UInt128?[] T2) => T1.ProtectedU().Div(T2.ToDouble());
#endif #endif
public static List<{typeName}> operator +(List<{typeName}> T1, {typeName} T2) => T1.Plus(T2._Value);
public static List<{typeName}?> operator +(List<{typeName}?> T1, {typeName} T2) => T1.Plus(T2._Value);
public static List<{typeName}> operator +({typeName} T1, List<{typeName}> T2) => T1._Value.Plus(T2);
public static List<{typeName}?> operator +({typeName} T1, List<{typeName}?> T2) => T1._Value.Plus(T2);
public static List<{typeName}> operator +(List<{typeName}> T1, {typeName}? T2) => T1.Plus(T2.Protected());
public static List<{typeName}?> operator +(List<{typeName}?> T1, {typeName}? T2) => T1.Plus(T2.Protected());
public static List<{typeName}> operator +({typeName}? T1, List<{typeName}> T2) => T1.Protected().Plus(T2);
public static List<{typeName}?> operator +({typeName}? T1, List<{typeName}?> T2) => T1.Protected().Plus(T2);
public static List<{typeName}> operator -(List<{typeName}> T1, {typeName} T2) => T1.Minus(T2._Value);
public static List<{typeName}?> operator -(List<{typeName}?> T1, {typeName} T2) => T1.Minus(T2._Value);
public static List<{typeName}> operator -({typeName} T1, List<{typeName}> T2) => T1._Value.Minus(T2);
public static List<{typeName}?> operator -({typeName} T1, List<{typeName}?> T2) => T1._Value.Minus(T2);
public static List<{typeName}> operator -(List<{typeName}> T1, {typeName}? T2) => T1.Minus(T2.Protected());
public static List<{typeName}?> operator -(List<{typeName}?> T1, {typeName}? T2) => T1.Minus(T2.Protected());
public static List<{typeName}> operator -({typeName}? T1, List<{typeName}> T2) => T1.Protected().Minus(T2);
public static List<{typeName}?> operator -({typeName}? T1, List<{typeName}?> T2) => T1.Protected().Minus(T2);
public static List<double> operator /({typeName} T1, List<{typeName}> T2) => T1.Div(T2);
public static List<double?> operator /({typeName} T1, List<{typeName}?> T2) => T1.Div(T2);
public static List<double> operator /(List<{typeName}> T1, {typeName} T2) => T1.Div(T2);
public static List<double?> operator /(List<{typeName}?> T1, {typeName} T2) => T1.Div(T2);
public static List<double> operator /({typeName}? T1, List<{typeName}> T2) => T1.ProtectedU().Div(T2);
public static List<double?> operator /({typeName}? T1, List<{typeName}?> T2) => T1.ProtectedU().Div(T2);
public static List<double> operator /(List<{typeName}> T1, {typeName}? T2) => T1.Div(T2.ProtectedU());
public static List<double?> operator /(List<{typeName}?> T1, {typeName}? T2) => T1.Div(T2.ProtectedU());
// double
public static List<{typeName}> operator *({typeName} T1, List<double> T2) => T1.Mul(T2);
public static List<{typeName}?> operator *({typeName} T1, List<double?> T2) => T1.Mul(T2);
public static List<{typeName}> operator *({typeName}? T1, List<double> T2) => T1.ProtectedU().Mul(T2);
public static List<{typeName}?> operator *({typeName}? T1, List<double?> T2) => T1.ProtectedU().Mul(T2);
public static List<{typeName}> operator *(List<double> T1, {typeName} T2) => T1.Mul(T2);
public static List<{typeName}?> operator *(List<double?> T1, {typeName} T2) => T1.Mul(T2);
public static List<{typeName}> operator *(List<double> T1, {typeName}? T2) => T1.Mul(T2.ProtectedU());
public static List<{typeName}?> operator *(List<double?> T1, {typeName}? T2) => T1.Mul(T2.ProtectedU());
public static List<{typeName}> operator /({typeName} T1, List<double> T2) => T1.Div(T2);
public static List<{typeName}?> operator /({typeName} T1, List<double?> T2) => T1.Div(T2);
public static List<{typeName}> operator /({typeName}? T1, List<double> T2) => T1.ProtectedU().Div(T2);
public static List<{typeName}?> operator /({typeName}? T1, List<double?> T2) => T1.ProtectedU().Div(T2);
// sbyte
public static List<{typeName}> operator *({typeName} T1, List<sbyte> T2) => T1.Mul(T2.ToDouble());
public static List<{typeName}?> operator *({typeName} T1, List<sbyte?> T2) => T1.Mul(T2.ToDouble());
public static List<{typeName}> operator *({typeName}? T1, List<sbyte> T2) => T1.ProtectedU().Mul(T2.ToDouble());
public static List<{typeName}?> operator *({typeName}? T1, List<sbyte?> T2) => T1.ProtectedU().Mul(T2.ToDouble());
public static List<{typeName}> operator *(List<sbyte> T1, {typeName} T2) => T1.ToDouble().Mul(T2);
public static List<{typeName}?> operator *(List<sbyte?> T1, {typeName} T2) => T1.ToDouble().Mul(T2);
public static List<{typeName}> operator *(List<sbyte> T1, {typeName}? T2) => T1.ToDouble().Mul(T2.ProtectedU());
public static List<{typeName}?> operator *(List<sbyte?> T1, {typeName}? T2) => T1.ToDouble().Mul(T2.ProtectedU());
public static List<{typeName}> operator /({typeName} T1, List<sbyte> T2) => T1.Div(T2.ToDouble());
public static List<{typeName}?> operator /({typeName} T1, List<sbyte?> T2) => T1.Div(T2.ToDouble());
public static List<{typeName}> operator /({typeName}? T1, List<sbyte> T2) => T1.ProtectedU().Div(T2.ToDouble());
public static List<{typeName}?> operator /({typeName}? T1, List<sbyte?> T2) => T1.ProtectedU().Div(T2.ToDouble());
// short
public static List<{typeName}> operator *({typeName} T1, List<short> T2) => T1.Mul(T2.ToDouble());
public static List<{typeName}?> operator *({typeName} T1, List<short?> T2) => T1.Mul(T2.ToDouble());
public static List<{typeName}> operator *({typeName}? T1, List<short> T2) => T1.ProtectedU().Mul(T2.ToDouble());
public static List<{typeName}?> operator *({typeName}? T1, List<short?> T2) => T1.ProtectedU().Mul(T2.ToDouble());
public static List<{typeName}> operator *(List<short> T1, {typeName} T2) => T1.ToDouble().Mul(T2);
public static List<{typeName}?> operator *(List<short?> T1, {typeName} T2) => T1.ToDouble().Mul(T2);
public static List<{typeName}> operator *(List<short> T1, {typeName}? T2) => T1.ToDouble().Mul(T2.ProtectedU());
public static List<{typeName}?> operator *(List<short?> T1, {typeName}? T2) => T1.ToDouble().Mul(T2.ProtectedU());
public static List<{typeName}> operator /({typeName} T1, List<short> T2) => T1.Div(T2.ToDouble());
public static List<{typeName}?> operator /({typeName} T1, List<short?> T2) => T1.Div(T2.ToDouble());
public static List<{typeName}> operator /({typeName}? T1, List<short> T2) => T1.ProtectedU().Div(T2.ToDouble());
public static List<{typeName}?> operator /({typeName}? T1, List<short?> T2) => T1.ProtectedU().Div(T2.ToDouble());
// int
public static List<{typeName}> operator *({typeName} T1, List<int> T2) => T1.Mul(T2.ToDouble());
public static List<{typeName}?> operator *({typeName} T1, List<int?> T2) => T1.Mul(T2.ToDouble());
public static List<{typeName}> operator *({typeName}? T1, List<int> T2) => T1.ProtectedU().Mul(T2.ToDouble());
public static List<{typeName}?> operator *({typeName}? T1, List<int?> T2) => T1.ProtectedU().Mul(T2.ToDouble());
public static List<{typeName}> operator *(List<int> T1, {typeName} T2) => T1.ToDouble().Mul(T2);
public static List<{typeName}?> operator *(List<int?> T1, {typeName} T2) => T1.ToDouble().Mul(T2);
public static List<{typeName}> operator *(List<int> T1, {typeName}? T2) => T1.ToDouble().Mul(T2.ProtectedU());
public static List<{typeName}?> operator *(List<int?> T1, {typeName}? T2) => T1.ToDouble().Mul(T2.ProtectedU());
public static List<{typeName}> operator /({typeName} T1, List<int> T2) => T1.Div(T2.ToDouble());
public static List<{typeName}?> operator /({typeName} T1, List<int?> T2) => T1.Div(T2.ToDouble());
public static List<{typeName}> operator /({typeName}? T1, List<int> T2) => T1.ProtectedU().Div(T2.ToDouble());
public static List<{typeName}?> operator /({typeName}? T1, List<int?> T2) => T1.ProtectedU().Div(T2.ToDouble());
// long
public static List<{typeName}> operator *({typeName} T1, List<long> T2) => T1.Mul(T2.ToDouble());
public static List<{typeName}?> operator *({typeName} T1, List<long?> T2) => T1.Mul(T2.ToDouble());
public static List<{typeName}> operator *({typeName}? T1, List<long> T2) => T1.ProtectedU().Mul(T2.ToDouble());
public static List<{typeName}?> operator *({typeName}? T1, List<long?> T2) => T1.ProtectedU().Mul(T2.ToDouble());
public static List<{typeName}> operator *(List<long> T1, {typeName} T2) => T1.ToDouble().Mul(T2);
public static List<{typeName}?> operator *(List<long?> T1, {typeName} T2) => T1.ToDouble().Mul(T2);
public static List<{typeName}> operator *(List<long> T1, {typeName}? T2) => T1.ToDouble().Mul(T2.ProtectedU());
public static List<{typeName}?> operator *(List<long?> T1, {typeName}? T2) => T1.ToDouble().Mul(T2.ProtectedU());
public static List<{typeName}> operator /({typeName} T1, List<long> T2) => T1.Div(T2.ToDouble());
public static List<{typeName}?> operator /({typeName} T1, List<long?> T2) => T1.Div(T2.ToDouble());
public static List<{typeName}> operator /({typeName}? T1, List<long> T2) => T1.ProtectedU().Div(T2.ToDouble());
public static List<{typeName}?> operator /({typeName}? T1, List<long?> T2) => T1.ProtectedU().Div(T2.ToDouble());
// byte
public static List<{typeName}> operator *({typeName} T1, List<byte> T2) => T1.Mul(T2.ToDouble());
public static List<{typeName}?> operator *({typeName} T1, List<byte?> T2) => T1.Mul(T2.ToDouble());
public static List<{typeName}> operator *({typeName}? T1, List<byte> T2) => T1.ProtectedU().Mul(T2.ToDouble());
public static List<{typeName}?> operator *({typeName}? T1, List<byte?> T2) => T1.ProtectedU().Mul(T2.ToDouble());
public static List<{typeName}> operator *(List<byte> T1, {typeName} T2) => T1.ToDouble().Mul(T2);
public static List<{typeName}?> operator *(List<byte?> T1, {typeName} T2) => T1.ToDouble().Mul(T2);
public static List<{typeName}> operator *(List<byte> T1, {typeName}? T2) => T1.ToDouble().Mul(T2.ProtectedU());
public static List<{typeName}?> operator *(List<byte?> T1, {typeName}? T2) => T1.ToDouble().Mul(T2.ProtectedU());
public static List<{typeName}> operator /({typeName} T1, List<byte> T2) => T1.Div(T2.ToDouble());
public static List<{typeName}?> operator /({typeName} T1, List<byte?> T2) => T1.Div(T2.ToDouble());
public static List<{typeName}> operator /({typeName}? T1, List<byte> T2) => T1.ProtectedU().Div(T2.ToDouble());
public static List<{typeName}?> operator /({typeName}? T1, List<byte?> T2) => T1.ProtectedU().Div(T2.ToDouble());
// ushort
public static List<{typeName}> operator *({typeName} T1, List<ushort> T2) => T1.Mul(T2.ToDouble());
public static List<{typeName}?> operator *({typeName} T1, List<ushort?> T2) => T1.Mul(T2.ToDouble());
public static List<{typeName}> operator *({typeName}? T1, List<ushort> T2) => T1.ProtectedU().Mul(T2.ToDouble());
public static List<{typeName}?> operator *({typeName}? T1, List<ushort?> T2) => T1.ProtectedU().Mul(T2.ToDouble());
public static List<{typeName}> operator *(List<ushort> T1, {typeName} T2) => T1.ToDouble().Mul(T2);
public static List<{typeName}?> operator *(List<ushort?> T1, {typeName} T2) => T1.ToDouble().Mul(T2);
public static List<{typeName}> operator *(List<ushort> T1, {typeName}? T2) => T1.ToDouble().Mul(T2.ProtectedU());
public static List<{typeName}?> operator *(List<ushort?> T1, {typeName}? T2) => T1.ToDouble().Mul(T2.ProtectedU());
public static List<{typeName}> operator /({typeName} T1, List<ushort> T2) => T1.Div(T2.ToDouble());
public static List<{typeName}?> operator /({typeName} T1, List<ushort?> T2) => T1.Div(T2.ToDouble());
public static List<{typeName}> operator /({typeName}? T1, List<ushort> T2) => T1.ProtectedU().Div(T2.ToDouble());
public static List<{typeName}?> operator /({typeName}? T1, List<ushort?> T2) => T1.ProtectedU().Div(T2.ToDouble());
// uint
public static List<{typeName}> operator *({typeName} T1, List<uint> T2) => T1.Mul(T2.ToDouble());
public static List<{typeName}?> operator *({typeName} T1, List<uint?> T2) => T1.Mul(T2.ToDouble());
public static List<{typeName}> operator *({typeName}? T1, List<uint> T2) => T1.ProtectedU().Mul(T2.ToDouble());
public static List<{typeName}?> operator *({typeName}? T1, List<uint?> T2) => T1.ProtectedU().Mul(T2.ToDouble());
public static List<{typeName}> operator *(List<uint> T1, {typeName} T2) => T1.ToDouble().Mul(T2);
public static List<{typeName}?> operator *(List<uint?> T1, {typeName} T2) => T1.ToDouble().Mul(T2);
public static List<{typeName}> operator *(List<uint> T1, {typeName}? T2) => T1.ToDouble().Mul(T2.ProtectedU());
public static List<{typeName}?> operator *(List<uint?> T1, {typeName}? T2) => T1.ToDouble().Mul(T2.ProtectedU());
public static List<{typeName}> operator /({typeName} T1, List<uint> T2) => T1.Div(T2.ToDouble());
public static List<{typeName}?> operator /({typeName} T1, List<uint?> T2) => T1.Div(T2.ToDouble());
public static List<{typeName}> operator /({typeName}? T1, List<uint> T2) => T1.ProtectedU().Div(T2.ToDouble());
public static List<{typeName}?> operator /({typeName}? T1, List<uint?> T2) => T1.ProtectedU().Div(T2.ToDouble());
// ulong
public static List<{typeName}> operator *({typeName} T1, List<ulong> T2) => T1.Mul(T2.ToDouble());
public static List<{typeName}?> operator *({typeName} T1, List<ulong?> T2) => T1.Mul(T2.ToDouble());
public static List<{typeName}> operator *({typeName}? T1, List<ulong> T2) => T1.ProtectedU().Mul(T2.ToDouble());
public static List<{typeName}?> operator *({typeName}? T1, List<ulong?> T2) => T1.ProtectedU().Mul(T2.ToDouble());
public static List<{typeName}> operator *(List<ulong> T1, {typeName} T2) => T1.ToDouble().Mul(T2);
public static List<{typeName}?> operator *(List<ulong?> T1, {typeName} T2) => T1.ToDouble().Mul(T2);
public static List<{typeName}> operator *(List<ulong> T1, {typeName}? T2) => T1.ToDouble().Mul(T2.ProtectedU());
public static List<{typeName}?> operator *(List<ulong?> T1, {typeName}? T2) => T1.ToDouble().Mul(T2.ProtectedU());
public static List<{typeName}> operator /({typeName} T1, List<ulong> T2) => T1.Div(T2.ToDouble());
public static List<{typeName}?> operator /({typeName} T1, List<ulong?> T2) => T1.Div(T2.ToDouble());
public static List<{typeName}> operator /({typeName}? T1, List<ulong> T2) => T1.ProtectedU().Div(T2.ToDouble());
public static List<{typeName}?> operator /({typeName}? T1, List<ulong?> T2) => T1.ProtectedU().Div(T2.ToDouble());
// nint
public static List<{typeName}> operator *({typeName} T1, List<nint> T2) => T1.Mul(T2.ToDouble());
public static List<{typeName}?> operator *({typeName} T1, List<nint?> T2) => T1.Mul(T2.ToDouble());
public static List<{typeName}> operator *({typeName}? T1, List<nint> T2) => T1.ProtectedU().Mul(T2.ToDouble());
public static List<{typeName}?> operator *({typeName}? T1, List<nint?> T2) => T1.ProtectedU().Mul(T2.ToDouble());
public static List<{typeName}> operator *(List<nint> T1, {typeName} T2) => T1.ToDouble().Mul(T2);
public static List<{typeName}?> operator *(List<nint?> T1, {typeName} T2) => T1.ToDouble().Mul(T2);
public static List<{typeName}> operator *(List<nint> T1, {typeName}? T2) => T1.ToDouble().Mul(T2.ProtectedU());
public static List<{typeName}?> operator *(List<nint?> T1, {typeName}? T2) => T1.ToDouble().Mul(T2.ProtectedU());
public static List<{typeName}> operator /({typeName} T1, List<nint> T2) => T1.Div(T2.ToDouble());
public static List<{typeName}?> operator /({typeName} T1, List<nint?> T2) => T1.Div(T2.ToDouble());
public static List<{typeName}> operator /({typeName}? T1, List<nint> T2) => T1.ProtectedU().Div(T2.ToDouble());
public static List<{typeName}?> operator /({typeName}? T1, List<nint?> T2) => T1.ProtectedU().Div(T2.ToDouble());
// nuint
public static List<{typeName}> operator *({typeName} T1, List<nuint> T2) => T1.Mul(T2.ToDouble());
public static List<{typeName}?> operator *({typeName} T1, List<nuint?> T2) => T1.Mul(T2.ToDouble());
public static List<{typeName}> operator *({typeName}? T1, List<nuint> T2) => T1.ProtectedU().Mul(T2.ToDouble());
public static List<{typeName}?> operator *({typeName}? T1, List<nuint?> T2) => T1.ProtectedU().Mul(T2.ToDouble());
public static List<{typeName}> operator *(List<nuint> T1, {typeName} T2) => T1.ToDouble().Mul(T2);
public static List<{typeName}?> operator *(List<nuint?> T1, {typeName} T2) => T1.ToDouble().Mul(T2);
public static List<{typeName}> operator *(List<nuint> T1, {typeName}? T2) => T1.ToDouble().Mul(T2.ProtectedU());
public static List<{typeName}?> operator *(List<nuint?> T1, {typeName}? T2) => T1.ToDouble().Mul(T2.ProtectedU());
public static List<{typeName}> operator /({typeName} T1, List<nuint> T2) => T1.Div(T2.ToDouble());
public static List<{typeName}?> operator /({typeName} T1, List<nuint?> T2) => T1.Div(T2.ToDouble());
public static List<{typeName}> operator /({typeName}? T1, List<nuint> T2) => T1.ProtectedU().Div(T2.ToDouble());
public static List<{typeName}?> operator /({typeName}? T1, List<nuint?> T2) => T1.ProtectedU().Div(T2.ToDouble());
// float
public static List<{typeName}> operator *({typeName} T1, List<float> T2) => T1.Mul(T2.ToDouble());
public static List<{typeName}?> operator *({typeName} T1, List<float?> T2) => T1.Mul(T2.ToDouble());
public static List<{typeName}> operator *({typeName}? T1, List<float> T2) => T1.ProtectedU().Mul(T2.ToDouble());
public static List<{typeName}?> operator *({typeName}? T1, List<float?> T2) => T1.ProtectedU().Mul(T2.ToDouble());
public static List<{typeName}> operator *(List<float> T1, {typeName} T2) => T1.ToDouble().Mul(T2);
public static List<{typeName}?> operator *(List<float?> T1, {typeName} T2) => T1.ToDouble().Mul(T2);
public static List<{typeName}> operator *(List<float> T1, {typeName}? T2) => T1.ToDouble().Mul(T2.ProtectedU());
public static List<{typeName}?> operator *(List<float?> T1, {typeName}? T2) => T1.ToDouble().Mul(T2.ProtectedU());
public static List<{typeName}> operator /({typeName} T1, List<float> T2) => T1.Div(T2.ToDouble());
public static List<{typeName}?> operator /({typeName} T1, List<float?> T2) => T1.Div(T2.ToDouble());
public static List<{typeName}> operator /({typeName}? T1, List<float> T2) => T1.ProtectedU().Div(T2.ToDouble());
public static List<{typeName}?> operator /({typeName}? T1, List<float?> T2) => T1.ProtectedU().Div(T2.ToDouble());
// decimal
public static List<{typeName}> operator *({typeName} T1, List<decimal> T2) => T1.Mul(T2.ToDouble());
public static List<{typeName}?> operator *({typeName} T1, List<decimal?> T2) => T1.Mul(T2.ToDouble());
public static List<{typeName}> operator *({typeName}? T1, List<decimal> T2) => T1.ProtectedU().Mul(T2.ToDouble());
public static List<{typeName}?> operator *({typeName}? T1, List<decimal?> T2) => T1.ProtectedU().Mul(T2.ToDouble());
public static List<{typeName}> operator *(List<decimal> T1, {typeName} T2) => T1.ToDouble().Mul(T2);
public static List<{typeName}?> operator *(List<decimal?> T1, {typeName} T2) => T1.ToDouble().Mul(T2);
public static List<{typeName}> operator *(List<decimal> T1, {typeName}? T2) => T1.ToDouble().Mul(T2.ProtectedU());
public static List<{typeName}?> operator *(List<decimal?> T1, {typeName}? T2) => T1.ToDouble().Mul(T2.ProtectedU());
public static List<{typeName}> operator /({typeName} T1, List<decimal> T2) => T1.Div(T2.ToDouble());
public static List<{typeName}?> operator /({typeName} T1, List<decimal?> T2) => T1.Div(T2.ToDouble());
public static List<{typeName}> operator /({typeName}? T1, List<decimal> T2) => T1.ProtectedU().Div(T2.ToDouble());
public static List<{typeName}?> operator /({typeName}? T1, List<decimal?> T2) => T1.ProtectedU().Div(T2.ToDouble());
#if NET7_0_OR_GREATER
// Int128
public static List<{typeName}> operator *({typeName} T1, List<Int128> T2) => T1.Mul(T2.ToDouble());
public static List<{typeName}?> operator *({typeName} T1, List<Int128?> T2) => T1.Mul(T2.ToDouble());
public static List<{typeName}> operator *({typeName}? T1, List<Int128> T2) => T1.ProtectedU().Mul(T2.ToDouble());
public static List<{typeName}?> operator *({typeName}? T1, List<Int128?> T2) => T1.ProtectedU().Mul(T2.ToDouble());
public static List<{typeName}> operator *(List<Int128> T1, {typeName} T2) => T1.ToDouble().Mul(T2);
public static List<{typeName}?> operator *(List<Int128?> T1, {typeName} T2) => T1.ToDouble().Mul(T2);
public static List<{typeName}> operator *(List<Int128> T1, {typeName}? T2) => T1.ToDouble().Mul(T2.ProtectedU());
public static List<{typeName}?> operator *(List<Int128?> T1, {typeName}? T2) => T1.ToDouble().Mul(T2.ProtectedU());
public static List<{typeName}> operator /({typeName} T1, List<Int128> T2) => T1.Div(T2.ToDouble());
public static List<{typeName}?> operator /({typeName} T1, List<Int128?> T2) => T1.Div(T2.ToDouble());
public static List<{typeName}> operator /({typeName}? T1, List<Int128> T2) => T1.ProtectedU().Div(T2.ToDouble());
public static List<{typeName}?> operator /({typeName}? T1, List<Int128?> T2) => T1.ProtectedU().Div(T2.ToDouble());
// UInt128
public static List<{typeName}> operator *({typeName} T1, List<UInt128> T2) => T1.Mul(T2.ToDouble());
public static List<{typeName}?> operator *({typeName} T1, List<UInt128?> T2) => T1.Mul(T2.ToDouble());
public static List<{typeName}> operator *({typeName}? T1, List<UInt128> T2) => T1.ProtectedU().Mul(T2.ToDouble());
public static List<{typeName}?> operator *({typeName}? T1, List<UInt128?> T2) => T1.ProtectedU().Mul(T2.ToDouble());
public static List<{typeName}> operator *(List<UInt128> T1, {typeName} T2) => T1.ToDouble().Mul(T2);
public static List<{typeName}?> operator *(List<UInt128?> T1, {typeName} T2) => T1.ToDouble().Mul(T2);
public static List<{typeName}> operator *(List<UInt128> T1, {typeName}? T2) => T1.ToDouble().Mul(T2.ProtectedU());
public static List<{typeName}?> operator *(List<UInt128?> T1, {typeName}? T2) => T1.ToDouble().Mul(T2.ProtectedU());
public static List<{typeName}> operator /({typeName} T1, List<UInt128> T2) => T1.Div(T2.ToDouble());
public static List<{typeName}?> operator /({typeName} T1, List<UInt128?> T2) => T1.Div(T2.ToDouble());
public static List<{typeName}> operator /({typeName}? T1, List<UInt128> T2) => T1.ProtectedU().Div(T2.ToDouble());
public static List<{typeName}?> operator /({typeName}? T1, List<UInt128?> T2) => T1.ProtectedU().Div(T2.ToDouble());
#endif
public static IEnumerable<{typeName}> operator +(IEnumerable<{typeName}> T1, {typeName} T2) => T1.Plus(T2._Value);
public static IEnumerable<{typeName}?> operator +(IEnumerable<{typeName}?> T1, {typeName} T2) => T1.Plus(T2._Value);
public static IEnumerable<{typeName}> operator +({typeName} T1, IEnumerable<{typeName}> T2) => T1._Value.Plus(T2);
public static IEnumerable<{typeName}?> operator +({typeName} T1, IEnumerable<{typeName}?> T2) => T1._Value.Plus(T2);
public static IEnumerable<{typeName}> operator +(IEnumerable<{typeName}> T1, {typeName}? T2) => T1.Plus(T2.Protected());
public static IEnumerable<{typeName}?> operator +(IEnumerable<{typeName}?> T1, {typeName}? T2) => T1.Plus(T2.Protected());
public static IEnumerable<{typeName}> operator +({typeName}? T1, IEnumerable<{typeName}> T2) => T1.Protected().Plus(T2);
public static IEnumerable<{typeName}?> operator +({typeName}? T1, IEnumerable<{typeName}?> T2) => T1.Protected().Plus(T2);
public static IEnumerable<{typeName}> operator -(IEnumerable<{typeName}> T1, {typeName} T2) => T1.Minus(T2._Value);
public static IEnumerable<{typeName}?> operator -(IEnumerable<{typeName}?> T1, {typeName} T2) => T1.Minus(T2._Value);
public static IEnumerable<{typeName}> operator -({typeName} T1, IEnumerable<{typeName}> T2) => T1._Value.Minus(T2);
public static IEnumerable<{typeName}?> operator -({typeName} T1, IEnumerable<{typeName}?> T2) => T1._Value.Minus(T2);
public static IEnumerable<{typeName}> operator -(IEnumerable<{typeName}> T1, {typeName}? T2) => T1.Minus(T2.Protected());
public static IEnumerable<{typeName}?> operator -(IEnumerable<{typeName}?> T1, {typeName}? T2) => T1.Minus(T2.Protected());
public static IEnumerable<{typeName}> operator -({typeName}? T1, IEnumerable<{typeName}> T2) => T1.Protected().Minus(T2);
public static IEnumerable<{typeName}?> operator -({typeName}? T1, IEnumerable<{typeName}?> T2) => T1.Protected().Minus(T2);
public static IEnumerable<double> operator /({typeName} T1, IEnumerable<{typeName}> T2) => T1.Div(T2);
public static IEnumerable<double?> operator /({typeName} T1, IEnumerable<{typeName}?> T2) => T1.Div(T2);
public static IEnumerable<double> operator /(IEnumerable<{typeName}> T1, {typeName} T2) => T1.Div(T2);
public static IEnumerable<double?> operator /(IEnumerable<{typeName}?> T1, {typeName} T2) => T1.Div(T2);
public static IEnumerable<double> operator /({typeName}? T1, IEnumerable<{typeName}> T2) => T1.ProtectedU().Div(T2);
public static IEnumerable<double?> operator /({typeName}? T1, IEnumerable<{typeName}?> T2) => T1.ProtectedU().Div(T2);
public static IEnumerable<double> operator /(IEnumerable<{typeName}> T1, {typeName}? T2) => T1.Div(T2.ProtectedU());
public static IEnumerable<double?> operator /(IEnumerable<{typeName}?> T1, {typeName}? T2) => T1.Div(T2.ProtectedU());
// double
public static IEnumerable<{typeName}> operator *({typeName} T1, IEnumerable<double> T2) => T1.Mul(T2);
public static IEnumerable<{typeName}?> operator *({typeName} T1, IEnumerable<double?> T2) => T1.Mul(T2);
public static IEnumerable<{typeName}> operator *({typeName}? T1, IEnumerable<double> T2) => T1.ProtectedU().Mul(T2);
public static IEnumerable<{typeName}?> operator *({typeName}? T1, IEnumerable<double?> T2) => T1.ProtectedU().Mul(T2);
public static IEnumerable<{typeName}> operator *(IEnumerable<double> T1, {typeName} T2) => T1.Mul(T2);
public static IEnumerable<{typeName}?> operator *(IEnumerable<double?> T1, {typeName} T2) => T1.Mul(T2);
public static IEnumerable<{typeName}> operator *(IEnumerable<double> T1, {typeName}? T2) => T1.Mul(T2.ProtectedU());
public static IEnumerable<{typeName}?> operator *(IEnumerable<double?> T1, {typeName}? T2) => T1.Mul(T2.ProtectedU());
public static IEnumerable<{typeName}> operator /({typeName} T1, IEnumerable<double> T2) => T1.Div(T2);
public static IEnumerable<{typeName}?> operator /({typeName} T1, IEnumerable<double?> T2) => T1.Div(T2);
public static IEnumerable<{typeName}> operator /({typeName}? T1, IEnumerable<double> T2) => T1.ProtectedU().Div(T2);
public static IEnumerable<{typeName}?> operator /({typeName}? T1, IEnumerable<double?> T2) => T1.ProtectedU().Div(T2);
// sbyte
public static IEnumerable<{typeName}> operator *({typeName} T1, IEnumerable<sbyte> T2) => T1.Mul(T2.ToDouble());
public static IEnumerable<{typeName}?> operator *({typeName} T1, IEnumerable<sbyte?> T2) => T1.Mul(T2.ToDouble());
public static IEnumerable<{typeName}> operator *({typeName}? T1, IEnumerable<sbyte> T2) => T1.ProtectedU().Mul(T2.ToDouble());
public static IEnumerable<{typeName}?> operator *({typeName}? T1, IEnumerable<sbyte?> T2) => T1.ProtectedU().Mul(T2.ToDouble());
public static IEnumerable<{typeName}> operator *(IEnumerable<sbyte> T1, {typeName} T2) => T1.ToDouble().Mul(T2);
public static IEnumerable<{typeName}?> operator *(IEnumerable<sbyte?> T1, {typeName} T2) => T1.ToDouble().Mul(T2);
public static IEnumerable<{typeName}> operator *(IEnumerable<sbyte> T1, {typeName}? T2) => T1.ToDouble().Mul(T2.ProtectedU());
public static IEnumerable<{typeName}?> operator *(IEnumerable<sbyte?> T1, {typeName}? T2) => T1.ToDouble().Mul(T2.ProtectedU());
public static IEnumerable<{typeName}> operator /({typeName} T1, IEnumerable<sbyte> T2) => T1.Div(T2.ToDouble());
public static IEnumerable<{typeName}?> operator /({typeName} T1, IEnumerable<sbyte?> T2) => T1.Div(T2.ToDouble());
public static IEnumerable<{typeName}> operator /({typeName}? T1, IEnumerable<sbyte> T2) => T1.ProtectedU().Div(T2.ToDouble());
public static IEnumerable<{typeName}?> operator /({typeName}? T1, IEnumerable<sbyte?> T2) => T1.ProtectedU().Div(T2.ToDouble());
// short
public static IEnumerable<{typeName}> operator *({typeName} T1, IEnumerable<short> T2) => T1.Mul(T2.ToDouble());
public static IEnumerable<{typeName}?> operator *({typeName} T1, IEnumerable<short?> T2) => T1.Mul(T2.ToDouble());
public static IEnumerable<{typeName}> operator *({typeName}? T1, IEnumerable<short> T2) => T1.ProtectedU().Mul(T2.ToDouble());
public static IEnumerable<{typeName}?> operator *({typeName}? T1, IEnumerable<short?> T2) => T1.ProtectedU().Mul(T2.ToDouble());
public static IEnumerable<{typeName}> operator *(IEnumerable<short> T1, {typeName} T2) => T1.ToDouble().Mul(T2);
public static IEnumerable<{typeName}?> operator *(IEnumerable<short?> T1, {typeName} T2) => T1.ToDouble().Mul(T2);
public static IEnumerable<{typeName}> operator *(IEnumerable<short> T1, {typeName}? T2) => T1.ToDouble().Mul(T2.ProtectedU());
public static IEnumerable<{typeName}?> operator *(IEnumerable<short?> T1, {typeName}? T2) => T1.ToDouble().Mul(T2.ProtectedU());
public static IEnumerable<{typeName}> operator /({typeName} T1, IEnumerable<short> T2) => T1.Div(T2.ToDouble());
public static IEnumerable<{typeName}?> operator /({typeName} T1, IEnumerable<short?> T2) => T1.Div(T2.ToDouble());
public static IEnumerable<{typeName}> operator /({typeName}? T1, IEnumerable<short> T2) => T1.ProtectedU().Div(T2.ToDouble());
public static IEnumerable<{typeName}?> operator /({typeName}? T1, IEnumerable<short?> T2) => T1.ProtectedU().Div(T2.ToDouble());
// int
public static IEnumerable<{typeName}> operator *({typeName} T1, IEnumerable<int> T2) => T1.Mul(T2.ToDouble());
public static IEnumerable<{typeName}?> operator *({typeName} T1, IEnumerable<int?> T2) => T1.Mul(T2.ToDouble());
public static IEnumerable<{typeName}> operator *({typeName}? T1, IEnumerable<int> T2) => T1.ProtectedU().Mul(T2.ToDouble());
public static IEnumerable<{typeName}?> operator *({typeName}? T1, IEnumerable<int?> T2) => T1.ProtectedU().Mul(T2.ToDouble());
public static IEnumerable<{typeName}> operator *(IEnumerable<int> T1, {typeName} T2) => T1.ToDouble().Mul(T2);
public static IEnumerable<{typeName}?> operator *(IEnumerable<int?> T1, {typeName} T2) => T1.ToDouble().Mul(T2);
public static IEnumerable<{typeName}> operator *(IEnumerable<int> T1, {typeName}? T2) => T1.ToDouble().Mul(T2.ProtectedU());
public static IEnumerable<{typeName}?> operator *(IEnumerable<int?> T1, {typeName}? T2) => T1.ToDouble().Mul(T2.ProtectedU());
public static IEnumerable<{typeName}> operator /({typeName} T1, IEnumerable<int> T2) => T1.Div(T2.ToDouble());
public static IEnumerable<{typeName}?> operator /({typeName} T1, IEnumerable<int?> T2) => T1.Div(T2.ToDouble());
public static IEnumerable<{typeName}> operator /({typeName}? T1, IEnumerable<int> T2) => T1.ProtectedU().Div(T2.ToDouble());
public static IEnumerable<{typeName}?> operator /({typeName}? T1, IEnumerable<int?> T2) => T1.ProtectedU().Div(T2.ToDouble());
// long
public static IEnumerable<{typeName}> operator *({typeName} T1, IEnumerable<long> T2) => T1.Mul(T2.ToDouble());
public static IEnumerable<{typeName}?> operator *({typeName} T1, IEnumerable<long?> T2) => T1.Mul(T2.ToDouble());
public static IEnumerable<{typeName}> operator *({typeName}? T1, IEnumerable<long> T2) => T1.ProtectedU().Mul(T2.ToDouble());
public static IEnumerable<{typeName}?> operator *({typeName}? T1, IEnumerable<long?> T2) => T1.ProtectedU().Mul(T2.ToDouble());
public static IEnumerable<{typeName}> operator *(IEnumerable<long> T1, {typeName} T2) => T1.ToDouble().Mul(T2);
public static IEnumerable<{typeName}?> operator *(IEnumerable<long?> T1, {typeName} T2) => T1.ToDouble().Mul(T2);
public static IEnumerable<{typeName}> operator *(IEnumerable<long> T1, {typeName}? T2) => T1.ToDouble().Mul(T2.ProtectedU());
public static IEnumerable<{typeName}?> operator *(IEnumerable<long?> T1, {typeName}? T2) => T1.ToDouble().Mul(T2.ProtectedU());
public static IEnumerable<{typeName}> operator /({typeName} T1, IEnumerable<long> T2) => T1.Div(T2.ToDouble());
public static IEnumerable<{typeName}?> operator /({typeName} T1, IEnumerable<long?> T2) => T1.Div(T2.ToDouble());
public static IEnumerable<{typeName}> operator /({typeName}? T1, IEnumerable<long> T2) => T1.ProtectedU().Div(T2.ToDouble());
public static IEnumerable<{typeName}?> operator /({typeName}? T1, IEnumerable<long?> T2) => T1.ProtectedU().Div(T2.ToDouble());
// byte
public static IEnumerable<{typeName}> operator *({typeName} T1, IEnumerable<byte> T2) => T1.Mul(T2.ToDouble());
public static IEnumerable<{typeName}?> operator *({typeName} T1, IEnumerable<byte?> T2) => T1.Mul(T2.ToDouble());
public static IEnumerable<{typeName}> operator *({typeName}? T1, IEnumerable<byte> T2) => T1.ProtectedU().Mul(T2.ToDouble());
public static IEnumerable<{typeName}?> operator *({typeName}? T1, IEnumerable<byte?> T2) => T1.ProtectedU().Mul(T2.ToDouble());
public static IEnumerable<{typeName}> operator *(IEnumerable<byte> T1, {typeName} T2) => T1.ToDouble().Mul(T2);
public static IEnumerable<{typeName}?> operator *(IEnumerable<byte?> T1, {typeName} T2) => T1.ToDouble().Mul(T2);
public static IEnumerable<{typeName}> operator *(IEnumerable<byte> T1, {typeName}? T2) => T1.ToDouble().Mul(T2.ProtectedU());
public static IEnumerable<{typeName}?> operator *(IEnumerable<byte?> T1, {typeName}? T2) => T1.ToDouble().Mul(T2.ProtectedU());
public static IEnumerable<{typeName}> operator /({typeName} T1, IEnumerable<byte> T2) => T1.Div(T2.ToDouble());
public static IEnumerable<{typeName}?> operator /({typeName} T1, IEnumerable<byte?> T2) => T1.Div(T2.ToDouble());
public static IEnumerable<{typeName}> operator /({typeName}? T1, IEnumerable<byte> T2) => T1.ProtectedU().Div(T2.ToDouble());
public static IEnumerable<{typeName}?> operator /({typeName}? T1, IEnumerable<byte?> T2) => T1.ProtectedU().Div(T2.ToDouble());
// ushort
public static IEnumerable<{typeName}> operator *({typeName} T1, IEnumerable<ushort> T2) => T1.Mul(T2.ToDouble());
public static IEnumerable<{typeName}?> operator *({typeName} T1, IEnumerable<ushort?> T2) => T1.Mul(T2.ToDouble());
public static IEnumerable<{typeName}> operator *({typeName}? T1, IEnumerable<ushort> T2) => T1.ProtectedU().Mul(T2.ToDouble());
public static IEnumerable<{typeName}?> operator *({typeName}? T1, IEnumerable<ushort?> T2) => T1.ProtectedU().Mul(T2.ToDouble());
public static IEnumerable<{typeName}> operator *(IEnumerable<ushort> T1, {typeName} T2) => T1.ToDouble().Mul(T2);
public static IEnumerable<{typeName}?> operator *(IEnumerable<ushort?> T1, {typeName} T2) => T1.ToDouble().Mul(T2);
public static IEnumerable<{typeName}> operator *(IEnumerable<ushort> T1, {typeName}? T2) => T1.ToDouble().Mul(T2.ProtectedU());
public static IEnumerable<{typeName}?> operator *(IEnumerable<ushort?> T1, {typeName}? T2) => T1.ToDouble().Mul(T2.ProtectedU());
public static IEnumerable<{typeName}> operator /({typeName} T1, IEnumerable<ushort> T2) => T1.Div(T2.ToDouble());
public static IEnumerable<{typeName}?> operator /({typeName} T1, IEnumerable<ushort?> T2) => T1.Div(T2.ToDouble());
public static IEnumerable<{typeName}> operator /({typeName}? T1, IEnumerable<ushort> T2) => T1.ProtectedU().Div(T2.ToDouble());
public static IEnumerable<{typeName}?> operator /({typeName}? T1, IEnumerable<ushort?> T2) => T1.ProtectedU().Div(T2.ToDouble());
// uint
public static IEnumerable<{typeName}> operator *({typeName} T1, IEnumerable<uint> T2) => T1.Mul(T2.ToDouble());
public static IEnumerable<{typeName}?> operator *({typeName} T1, IEnumerable<uint?> T2) => T1.Mul(T2.ToDouble());
public static IEnumerable<{typeName}> operator *({typeName}? T1, IEnumerable<uint> T2) => T1.ProtectedU().Mul(T2.ToDouble());
public static IEnumerable<{typeName}?> operator *({typeName}? T1, IEnumerable<uint?> T2) => T1.ProtectedU().Mul(T2.ToDouble());
public static IEnumerable<{typeName}> operator *(IEnumerable<uint> T1, {typeName} T2) => T1.ToDouble().Mul(T2);
public static IEnumerable<{typeName}?> operator *(IEnumerable<uint?> T1, {typeName} T2) => T1.ToDouble().Mul(T2);
public static IEnumerable<{typeName}> operator *(IEnumerable<uint> T1, {typeName}? T2) => T1.ToDouble().Mul(T2.ProtectedU());
public static IEnumerable<{typeName}?> operator *(IEnumerable<uint?> T1, {typeName}? T2) => T1.ToDouble().Mul(T2.ProtectedU());
public static IEnumerable<{typeName}> operator /({typeName} T1, IEnumerable<uint> T2) => T1.Div(T2.ToDouble());
public static IEnumerable<{typeName}?> operator /({typeName} T1, IEnumerable<uint?> T2) => T1.Div(T2.ToDouble());
public static IEnumerable<{typeName}> operator /({typeName}? T1, IEnumerable<uint> T2) => T1.ProtectedU().Div(T2.ToDouble());
public static IEnumerable<{typeName}?> operator /({typeName}? T1, IEnumerable<uint?> T2) => T1.ProtectedU().Div(T2.ToDouble());
// ulong
public static IEnumerable<{typeName}> operator *({typeName} T1, IEnumerable<ulong> T2) => T1.Mul(T2.ToDouble());
public static IEnumerable<{typeName}?> operator *({typeName} T1, IEnumerable<ulong?> T2) => T1.Mul(T2.ToDouble());
public static IEnumerable<{typeName}> operator *({typeName}? T1, IEnumerable<ulong> T2) => T1.ProtectedU().Mul(T2.ToDouble());
public static IEnumerable<{typeName}?> operator *({typeName}? T1, IEnumerable<ulong?> T2) => T1.ProtectedU().Mul(T2.ToDouble());
public static IEnumerable<{typeName}> operator *(IEnumerable<ulong> T1, {typeName} T2) => T1.ToDouble().Mul(T2);
public static IEnumerable<{typeName}?> operator *(IEnumerable<ulong?> T1, {typeName} T2) => T1.ToDouble().Mul(T2);
public static IEnumerable<{typeName}> operator *(IEnumerable<ulong> T1, {typeName}? T2) => T1.ToDouble().Mul(T2.ProtectedU());
public static IEnumerable<{typeName}?> operator *(IEnumerable<ulong?> T1, {typeName}? T2) => T1.ToDouble().Mul(T2.ProtectedU());
public static IEnumerable<{typeName}> operator /({typeName} T1, IEnumerable<ulong> T2) => T1.Div(T2.ToDouble());
public static IEnumerable<{typeName}?> operator /({typeName} T1, IEnumerable<ulong?> T2) => T1.Div(T2.ToDouble());
public static IEnumerable<{typeName}> operator /({typeName}? T1, IEnumerable<ulong> T2) => T1.ProtectedU().Div(T2.ToDouble());
public static IEnumerable<{typeName}?> operator /({typeName}? T1, IEnumerable<ulong?> T2) => T1.ProtectedU().Div(T2.ToDouble());
// nint
public static IEnumerable<{typeName}> operator *({typeName} T1, IEnumerable<nint> T2) => T1.Mul(T2.ToDouble());
public static IEnumerable<{typeName}?> operator *({typeName} T1, IEnumerable<nint?> T2) => T1.Mul(T2.ToDouble());
public static IEnumerable<{typeName}> operator *({typeName}? T1, IEnumerable<nint> T2) => T1.ProtectedU().Mul(T2.ToDouble());
public static IEnumerable<{typeName}?> operator *({typeName}? T1, IEnumerable<nint?> T2) => T1.ProtectedU().Mul(T2.ToDouble());
public static IEnumerable<{typeName}> operator *(IEnumerable<nint> T1, {typeName} T2) => T1.ToDouble().Mul(T2);
public static IEnumerable<{typeName}?> operator *(IEnumerable<nint?> T1, {typeName} T2) => T1.ToDouble().Mul(T2);
public static IEnumerable<{typeName}> operator *(IEnumerable<nint> T1, {typeName}? T2) => T1.ToDouble().Mul(T2.ProtectedU());
public static IEnumerable<{typeName}?> operator *(IEnumerable<nint?> T1, {typeName}? T2) => T1.ToDouble().Mul(T2.ProtectedU());
public static IEnumerable<{typeName}> operator /({typeName} T1, IEnumerable<nint> T2) => T1.Div(T2.ToDouble());
public static IEnumerable<{typeName}?> operator /({typeName} T1, IEnumerable<nint?> T2) => T1.Div(T2.ToDouble());
public static IEnumerable<{typeName}> operator /({typeName}? T1, IEnumerable<nint> T2) => T1.ProtectedU().Div(T2.ToDouble());
public static IEnumerable<{typeName}?> operator /({typeName}? T1, IEnumerable<nint?> T2) => T1.ProtectedU().Div(T2.ToDouble());
// nuint
public static IEnumerable<{typeName}> operator *({typeName} T1, IEnumerable<nuint> T2) => T1.Mul(T2.ToDouble());
public static IEnumerable<{typeName}?> operator *({typeName} T1, IEnumerable<nuint?> T2) => T1.Mul(T2.ToDouble());
public static IEnumerable<{typeName}> operator *({typeName}? T1, IEnumerable<nuint> T2) => T1.ProtectedU().Mul(T2.ToDouble());
public static IEnumerable<{typeName}?> operator *({typeName}? T1, IEnumerable<nuint?> T2) => T1.ProtectedU().Mul(T2.ToDouble());
public static IEnumerable<{typeName}> operator *(IEnumerable<nuint> T1, {typeName} T2) => T1.ToDouble().Mul(T2);
public static IEnumerable<{typeName}?> operator *(IEnumerable<nuint?> T1, {typeName} T2) => T1.ToDouble().Mul(T2);
public static IEnumerable<{typeName}> operator *(IEnumerable<nuint> T1, {typeName}? T2) => T1.ToDouble().Mul(T2.ProtectedU());
public static IEnumerable<{typeName}?> operator *(IEnumerable<nuint?> T1, {typeName}? T2) => T1.ToDouble().Mul(T2.ProtectedU());
public static IEnumerable<{typeName}> operator /({typeName} T1, IEnumerable<nuint> T2) => T1.Div(T2.ToDouble());
public static IEnumerable<{typeName}?> operator /({typeName} T1, IEnumerable<nuint?> T2) => T1.Div(T2.ToDouble());
public static IEnumerable<{typeName}> operator /({typeName}? T1, IEnumerable<nuint> T2) => T1.ProtectedU().Div(T2.ToDouble());
public static IEnumerable<{typeName}?> operator /({typeName}? T1, IEnumerable<nuint?> T2) => T1.ProtectedU().Div(T2.ToDouble());
// float
public static IEnumerable<{typeName}> operator *({typeName} T1, IEnumerable<float> T2) => T1.Mul(T2.ToDouble());
public static IEnumerable<{typeName}?> operator *({typeName} T1, IEnumerable<float?> T2) => T1.Mul(T2.ToDouble());
public static IEnumerable<{typeName}> operator *({typeName}? T1, IEnumerable<float> T2) => T1.ProtectedU().Mul(T2.ToDouble());
public static IEnumerable<{typeName}?> operator *({typeName}? T1, IEnumerable<float?> T2) => T1.ProtectedU().Mul(T2.ToDouble());
public static IEnumerable<{typeName}> operator *(IEnumerable<float> T1, {typeName} T2) => T1.ToDouble().Mul(T2);
public static IEnumerable<{typeName}?> operator *(IEnumerable<float?> T1, {typeName} T2) => T1.ToDouble().Mul(T2);
public static IEnumerable<{typeName}> operator *(IEnumerable<float> T1, {typeName}? T2) => T1.ToDouble().Mul(T2.ProtectedU());
public static IEnumerable<{typeName}?> operator *(IEnumerable<float?> T1, {typeName}? T2) => T1.ToDouble().Mul(T2.ProtectedU());
public static IEnumerable<{typeName}> operator /({typeName} T1, IEnumerable<float> T2) => T1.Div(T2.ToDouble());
public static IEnumerable<{typeName}?> operator /({typeName} T1, IEnumerable<float?> T2) => T1.Div(T2.ToDouble());
public static IEnumerable<{typeName}> operator /({typeName}? T1, IEnumerable<float> T2) => T1.ProtectedU().Div(T2.ToDouble());
public static IEnumerable<{typeName}?> operator /({typeName}? T1, IEnumerable<float?> T2) => T1.ProtectedU().Div(T2.ToDouble());
// decimal
public static IEnumerable<{typeName}> operator *({typeName} T1, IEnumerable<decimal> T2) => T1.Mul(T2.ToDouble());
public static IEnumerable<{typeName}?> operator *({typeName} T1, IEnumerable<decimal?> T2) => T1.Mul(T2.ToDouble());
public static IEnumerable<{typeName}> operator *({typeName}? T1, IEnumerable<decimal> T2) => T1.ProtectedU().Mul(T2.ToDouble());
public static IEnumerable<{typeName}?> operator *({typeName}? T1, IEnumerable<decimal?> T2) => T1.ProtectedU().Mul(T2.ToDouble());
public static IEnumerable<{typeName}> operator *(IEnumerable<decimal> T1, {typeName} T2) => T1.ToDouble().Mul(T2);
public static IEnumerable<{typeName}?> operator *(IEnumerable<decimal?> T1, {typeName} T2) => T1.ToDouble().Mul(T2);
public static IEnumerable<{typeName}> operator *(IEnumerable<decimal> T1, {typeName}? T2) => T1.ToDouble().Mul(T2.ProtectedU());
public static IEnumerable<{typeName}?> operator *(IEnumerable<decimal?> T1, {typeName}? T2) => T1.ToDouble().Mul(T2.ProtectedU());
public static IEnumerable<{typeName}> operator /({typeName} T1, IEnumerable<decimal> T2) => T1.Div(T2.ToDouble());
public static IEnumerable<{typeName}?> operator /({typeName} T1, IEnumerable<decimal?> T2) => T1.Div(T2.ToDouble());
public static IEnumerable<{typeName}> operator /({typeName}? T1, IEnumerable<decimal> T2) => T1.ProtectedU().Div(T2.ToDouble());
public static IEnumerable<{typeName}?> operator /({typeName}? T1, IEnumerable<decimal?> T2) => T1.ProtectedU().Div(T2.ToDouble());
#if NET7_0_OR_GREATER
// Int128
public static IEnumerable<{typeName}> operator *({typeName} T1, IEnumerable<Int128> T2) => T1.Mul(T2.ToDouble());
public static IEnumerable<{typeName}?> operator *({typeName} T1, IEnumerable<Int128?> T2) => T1.Mul(T2.ToDouble());
public static IEnumerable<{typeName}> operator *({typeName}? T1, IEnumerable<Int128> T2) => T1.ProtectedU().Mul(T2.ToDouble());
public static IEnumerable<{typeName}?> operator *({typeName}? T1, IEnumerable<Int128?> T2) => T1.ProtectedU().Mul(T2.ToDouble());
public static IEnumerable<{typeName}> operator *(IEnumerable<Int128> T1, {typeName} T2) => T1.ToDouble().Mul(T2);
public static IEnumerable<{typeName}?> operator *(IEnumerable<Int128?> T1, {typeName} T2) => T1.ToDouble().Mul(T2);
public static IEnumerable<{typeName}> operator *(IEnumerable<Int128> T1, {typeName}? T2) => T1.ToDouble().Mul(T2.ProtectedU());
public static IEnumerable<{typeName}?> operator *(IEnumerable<Int128?> T1, {typeName}? T2) => T1.ToDouble().Mul(T2.ProtectedU());
public static IEnumerable<{typeName}> operator /({typeName} T1, IEnumerable<Int128> T2) => T1.Div(T2.ToDouble());
public static IEnumerable<{typeName}?> operator /({typeName} T1, IEnumerable<Int128?> T2) => T1.Div(T2.ToDouble());
public static IEnumerable<{typeName}> operator /({typeName}? T1, IEnumerable<Int128> T2) => T1.ProtectedU().Div(T2.ToDouble());
public static IEnumerable<{typeName}?> operator /({typeName}? T1, IEnumerable<Int128?> T2) => T1.ProtectedU().Div(T2.ToDouble());
// UInt128
public static IEnumerable<{typeName}> operator *({typeName} T1, IEnumerable<UInt128> T2) => T1.Mul(T2.ToDouble());
public static IEnumerable<{typeName}?> operator *({typeName} T1, IEnumerable<UInt128?> T2) => T1.Mul(T2.ToDouble());
public static IEnumerable<{typeName}> operator *({typeName}? T1, IEnumerable<UInt128> T2) => T1.ProtectedU().Mul(T2.ToDouble());
public static IEnumerable<{typeName}?> operator *({typeName}? T1, IEnumerable<UInt128?> T2) => T1.ProtectedU().Mul(T2.ToDouble());
public static IEnumerable<{typeName}> operator *(IEnumerable<UInt128> T1, {typeName} T2) => T1.ToDouble().Mul(T2);
public static IEnumerable<{typeName}?> operator *(IEnumerable<UInt128?> T1, {typeName} T2) => T1.ToDouble().Mul(T2);
public static IEnumerable<{typeName}> operator *(IEnumerable<UInt128> T1, {typeName}? T2) => T1.ToDouble().Mul(T2.ProtectedU());
public static IEnumerable<{typeName}?> operator *(IEnumerable<UInt128?> T1, {typeName}? T2) => T1.ToDouble().Mul(T2.ProtectedU());
public static IEnumerable<{typeName}> operator /({typeName} T1, IEnumerable<UInt128> T2) => T1.Div(T2.ToDouble());
public static IEnumerable<{typeName}?> operator /({typeName} T1, IEnumerable<UInt128?> T2) => T1.Div(T2.ToDouble());
public static IEnumerable<{typeName}> operator /({typeName}? T1, IEnumerable<UInt128> T2) => T1.ProtectedU().Div(T2.ToDouble());
public static IEnumerable<{typeName}?> operator /({typeName}? T1, IEnumerable<UInt128?> T2) => T1.ProtectedU().Div(T2.ToDouble());
#endif
} }
public static class {typeName}Extensions public static class {typeName}Extensions
@@ -619,16 +1111,6 @@ public static class {typeName}Extensions
[MethodImpl(MethodImplOptions.AggressiveInlining)] public static List<{typeName}?> Div( [MethodImpl(MethodImplOptions.AggressiveInlining)] public static List<{typeName}?> Div(
this double dividend, List<{typeName}?> units) => dividend.Div<{typeName}>(units); this double dividend, List<{typeName}?> units) => dividend.Div<{typeName}>(units);
// === ICollection<T> ===
[MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Div(this ICollection<{typeName}> units,
double divisor, Span<{typeName}> destination) => units.Div<{typeName}>(divisor, destination);
[MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Div(this ICollection<{typeName}?> units,
double divisor, Span<{typeName}?> destination) => units.Div<{typeName}>(divisor, destination);
[MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Div(this double dividend,
ICollection<{typeName}> units, Span<{typeName}> destination) => dividend.Div<{typeName}>(units, destination);
[MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Div(this double dividend,
ICollection<{typeName}?> units, Span<{typeName}?> destination) => dividend.Div<{typeName}>(units, destination);
// === IReadOnlyCollection<T> === // === IReadOnlyCollection<T> ===
[MethodImpl(MethodImplOptions.AggressiveInlining)]public static void Div(this IReadOnlyCollection<{typeName}> units, [MethodImpl(MethodImplOptions.AggressiveInlining)]public static void Div(this IReadOnlyCollection<{typeName}> units,
double divisor, Span<{typeName}> destination) => units.Div<{typeName}>(divisor, destination); double divisor, Span<{typeName}> destination) => units.Div<{typeName}>(divisor, destination);
@@ -684,16 +1166,6 @@ public static class {typeName}Extensions
[MethodImpl(MethodImplOptions.AggressiveInlining)] public static List<{typeName}?> Mul( [MethodImpl(MethodImplOptions.AggressiveInlining)] public static List<{typeName}?> Mul(
this double multiplicator, List<{typeName}?> units) => units.Mul<{typeName}>(multiplicator); this double multiplicator, List<{typeName}?> units) => units.Mul<{typeName}>(multiplicator);
// === ICollection<T> ===
[MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Mul(this ICollection<{typeName}> units,
double multiplicator, Span<{typeName}> destination) => units.Mul<{typeName}>(multiplicator, destination);
[MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Mul(this ICollection<{typeName}?> units,
double multiplicator, Span<{typeName}?> destination) => units.Mul<{typeName}>(multiplicator, destination);
[MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Mul(this double multiplicator,
ICollection<{typeName}> units, Span<{typeName}> destination) => units.Mul<{typeName}>(multiplicator, destination);
[MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Mul(this double multiplicator,
ICollection<{typeName}?> units, Span<{typeName}?> destination) => units.Mul<{typeName}>(multiplicator, destination);
// === IReadOnlyCollection<T> === // === IReadOnlyCollection<T> ===
[MethodImpl(MethodImplOptions.AggressiveInlining)]public static void Mul(this IReadOnlyCollection<{typeName}> units, [MethodImpl(MethodImplOptions.AggressiveInlining)]public static void Mul(this IReadOnlyCollection<{typeName}> units,
double multiplicator, Span<{typeName}> destination) => units.Mul<{typeName}>(multiplicator, destination); double multiplicator, Span<{typeName}> destination) => units.Mul<{typeName}>(multiplicator, destination);
@@ -734,12 +1206,6 @@ public static class {typeName}Extensions
public static {typeName} Max(this List<{typeName}> list) => AggregateUnitExtensions.Max(list); public static {typeName} Max(this List<{typeName}> list) => AggregateUnitExtensions.Max(list);
public static {typeName} Min(this List<{typeName}> list) => AggregateUnitExtensions.Min(list); public static {typeName} Min(this List<{typeName}> list) => AggregateUnitExtensions.Min(list);
// === ICollection<T> ===
public static {typeName} Sum(this ICollection<{typeName}> collection) => AggregateUnitExtensions.Sum(collection);
public static {typeName} Avg(this ICollection<{typeName}> collection) => AggregateUnitExtensions.Avg(collection);
public static {typeName} Max(this ICollection<{typeName}> collection) => AggregateUnitExtensions.Max(collection);
public static {typeName} Min(this ICollection<{typeName}> collection) => AggregateUnitExtensions.Min(collection);
// === IReadOnlyCollection<T> === // === IReadOnlyCollection<T> ===
public static {typeName} Sum(this IReadOnlyCollection<{typeName}> collection) => AggregateUnitExtensions.Sum(collection); public static {typeName} Sum(this IReadOnlyCollection<{typeName}> collection) => AggregateUnitExtensions.Sum(collection);
public static {typeName} Avg(this IReadOnlyCollection<{typeName}> collection) => AggregateUnitExtensions.Avg(collection); public static {typeName} Avg(this IReadOnlyCollection<{typeName}> collection) => AggregateUnitExtensions.Avg(collection);
@@ -770,12 +1236,6 @@ public static class {typeName}Extensions
public static {typeName} Max(this List<{typeName}?> list) => AggregateUnitExtensions.Max(list); public static {typeName} Max(this List<{typeName}?> list) => AggregateUnitExtensions.Max(list);
public static {typeName} Min(this List<{typeName}?> list) => AggregateUnitExtensions.Min(list); public static {typeName} Min(this List<{typeName}?> list) => AggregateUnitExtensions.Min(list);
// === ICollection<T> ===
public static {typeName} Sum(this ICollection<{typeName}?> collection) => AggregateUnitExtensions.Sum(collection);
public static {typeName} Avg(this ICollection<{typeName}?> collection) => AggregateUnitExtensions.Avg(collection);
public static {typeName} Max(this ICollection<{typeName}?> collection) => AggregateUnitExtensions.Max(collection);
public static {typeName} Min(this ICollection<{typeName}?> collection) => AggregateUnitExtensions.Min(collection);
// === IReadOnlyCollection<T> === // === IReadOnlyCollection<T> ===
public static {typeName} Sum(this IReadOnlyCollection<{typeName}?> collection) => AggregateUnitExtensions.Sum(collection); public static {typeName} Sum(this IReadOnlyCollection<{typeName}?> collection) => AggregateUnitExtensions.Sum(collection);
public static {typeName} Avg(this IReadOnlyCollection<{typeName}?> collection) => AggregateUnitExtensions.Avg(collection); public static {typeName} Avg(this IReadOnlyCollection<{typeName}?> collection) => AggregateUnitExtensions.Avg(collection);

View File

@@ -0,0 +1,170 @@
using System.Reflection;
namespace QWERTYkez.Mensura.Tests;
public class AggregateUnitExtensions
{
// Вспомогательный метод для создания объекта Length.
// Если у вас используется фабричный метод (например, Length.FromMeters), замените код внутри.
private static Length CreateLength(double value) => value * Length._MilliMeter;
#region Инфраструктура Рефлексии (Invoker)
private delegate Length SpanDelegate(ReadOnlySpan<Length> units);
private delegate Length SpanNullableDelegate(ReadOnlySpan<Length?> units);
private static class Invoker
{
private static readonly Type ExtType;
static Invoker()
{
// Находим внутренний класс AggregateUnitExtensions в целевой сборке
ExtType = typeof(Length).Assembly.GetType("QWERTYkez.Mensura.Extensions.AggregateUnitExtensions")
?? throw new InvalidOperationException("Не удалось найти класс AggregateUnitExtensions через рефлексию.");
}
public static Length InvokeSpan(string methodName, ReadOnlySpan<Length> data)
{
var method = FindMethod(methodName, typeof(ReadOnlySpan<>), isNullable: false);
var closedMethod = method.MakeGenericMethod(typeof(Length));
var del = closedMethod.CreateDelegate<SpanDelegate>();
return del(data);
}
public static Length InvokeSpanNullable(string methodName, ReadOnlySpan<Length?> data)
{
var method = FindMethod(methodName, typeof(ReadOnlySpan<>), isNullable: true);
var closedMethod = method.MakeGenericMethod(typeof(Length));
var del = closedMethod.CreateDelegate<SpanNullableDelegate>();
return del(data);
}
public static Length InvokeCollection(string methodName, Type genericContainer, object data, bool isNullable)
{
var method = FindMethod(methodName, genericContainer, isNullable);
var closedMethod = method.MakeGenericMethod(typeof(Length));
return (Length)closedMethod.Invoke(null, [data])!;
}
private static MethodInfo FindMethod(string name, Type genericContainerType, bool isNullable)
{
var methods = ExtType.GetMethods(BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public)
.Where(m => m.Name == name);
foreach (var method in methods)
{
var parameters = method.GetParameters();
if (parameters.Length != 1) continue;
var paramType = parameters[0].ParameterType;
if (!paramType.IsGenericType) continue;
// Проверяем базовый контейнер (List<>, IEnumerable<>, ReadOnlySpan<>)
if (paramType.GetGenericTypeDefinition() != genericContainerType) continue;
// Проверяем внутренний аргумент типа на Nullable
var genericArgument = paramType.GetGenericArguments()[0];
bool currentIsNullable = genericArgument.IsGenericType &&
genericArgument.GetGenericTypeDefinition() == typeof(Nullable<>);
if (currentIsNullable == isNullable)
{
return method;
}
}
throw new MethodAccessException($"Метод {name} для контейнера {genericContainerType.Name} (Nullable: {isNullable}) не найден.");
}
}
#endregion
#region Тесты для НЕ-выделяющих Nullable типов (Обычные структуры)
private readonly Length[] _standardData = [CreateLength(10), CreateLength(20), CreateLength(30)];
private readonly Length _expectedSum = CreateLength(60);
private readonly Length _expectedAvg = CreateLength(20);
private readonly Length _expectedMax = CreateLength(30);
private readonly Length _expectedMin = CreateLength(10);
[Theory]
[InlineData("Sum")]
[InlineData("Avg")]
[InlineData("Max")]
[InlineData("Min")]
public void StandardContainers_ShouldCalculateCorrectly(string operation)
{
// Набор ожидаемых значений
Length expected = operation switch
{
"Sum" => _expectedSum,
"Avg" => _expectedAvg,
"Max" => _expectedMax,
"Min" => _expectedMin,
_ => throw new ArgumentException(operation)
};
// 1. Тест ReadOnlySpan<T>
ReadOnlySpan<Length> span = _standardData;
Assert.Equal(expected, Invoker.InvokeSpan(operation, span));
// 2. Тест List<T>
var list = _standardData.ToList();
Assert.Equal(expected, Invoker.InvokeCollection(operation, typeof(List<>), list, isNullable: false));
// 3. Тест IReadOnlyCollection<T>
IReadOnlyCollection<Length> readOnlyCollection = _standardData;
Assert.Equal(expected, Invoker.InvokeCollection(operation, typeof(IReadOnlyCollection<>), readOnlyCollection, isNullable: false));
// 4. Тест IEnumerable<T>
IEnumerable<Length> enumerable = _standardData.Select(x => x);
Assert.Equal(expected, Invoker.InvokeCollection(operation, typeof(IEnumerable<>), enumerable, isNullable: false));
}
#endregion
#region Тесты для Nullable типов (T?)
// Тестируем смесь значений с null-элементами
private readonly Length?[] _nullableData = [CreateLength(10), null, CreateLength(30)];
private readonly Length _expectedNullSum = CreateLength(40);
private readonly Length _expectedNullAvg = CreateLength(20); // 40 / 2 значения
private readonly Length _expectedNullMax = CreateLength(30);
private readonly Length _expectedNullMin = CreateLength(10);
[Theory]
[InlineData("Sum")]
[InlineData("Avg")]
[InlineData("Max")]
[InlineData("Min")]
public void NullableContainers_ShouldCalculateCorrectly(string operation)
{
Length expected = operation switch
{
"Sum" => _expectedNullSum,
"Avg" => _expectedNullAvg,
"Max" => _expectedNullMax,
"Min" => _expectedNullMin,
_ => throw new ArgumentException(operation)
};
// 1. Тест ReadOnlySpan<T?>
ReadOnlySpan<Length?> span = _nullableData;
Assert.Equal(expected, Invoker.InvokeSpanNullable(operation, span));
// 2. Тест List<T?>
var list = _nullableData.ToList();
Assert.Equal(expected, Invoker.InvokeCollection(operation, typeof(List<>), list, isNullable: true));
// 3. Тест IReadOnlyCollection<T?>
IReadOnlyCollection<Length?> readOnlyCollection = _nullableData;
Assert.Equal(expected, Invoker.InvokeCollection(operation, typeof(IReadOnlyCollection<>), readOnlyCollection, isNullable: true));
// 4. Тест IEnumerable<T?>
IEnumerable<Length?> enumerable = _nullableData.Select(x => x);
Assert.Equal(expected, Invoker.InvokeCollection(operation, typeof(IEnumerable<>), enumerable, isNullable: true));
}
#endregion
}

View File

@@ -0,0 +1,165 @@
namespace QWERTYkez.Mensura.Tests;
public class CastExtensions
{
private const double NormalValue1 = 42.42;
private const double NormalValue2 = 100.05;
#region 1. Слабое место: Побитовая идентичность и спец-значения (NaN/Inf)
// Так как используется Unsafe.As, нужно убедиться, что битовая сетка double
// не ломается при сохранении пограничных значений (NaN, Infinity).
[Theory]
[InlineData(NormalValue1)]
[InlineData(double.NaN)]
[InlineData(double.PositiveInfinity)]
[InlineData(double.NegativeInfinity)]
[InlineData(double.Epsilon)]
public void SingleConversion_Should_Preserve_Exact_BitPattern_For_Special_Doubles(double specialValue)
{
// Act
Length unit = specialValue.ToUnit<Length>();
double recovered = unit.ToDouble();
// Assert
// Используем BitConverter, чтобы проверить идентичность на уровне битов (особенно важно для NaN)
long originalBits = BitConverter.DoubleToInt64Bits(specialValue);
long recoveredBits = BitConverter.DoubleToInt64Bits(recovered);
Assert.Equal(originalBits, recoveredBits);
}
#endregion
#region 2. Слабое место: Взрыв CLR при изменении емкости (EnsureCapacity)
// При ReCast списков подменяется MethodTable самого списка и его внутреннего массива.
// Если мы начнем добавлять элементы, список выделит новый массив через Array.CreateInstance.
// Если тип в MethodTable не совпадет с тем, что ожидает среда исполнения, GC или рантайм упадет.
[Fact]
public void List_ReCast_Should_Survive_Massive_Resize_And_GC_Collections()
{
// Arrange
var originalList = new List<Length> { (Length)NormalValue1, (Length)NormalValue2 };
// Act
List<double> morphedList = originalList.ReCast();
// Провоцируем многократное выделение новой памяти (Resize внутреннего массива)
for (int i = 0; i < 100; i++)
{
morphedList.Add(i * 1.1);
}
// Вызываем сборщик мусора, чтобы проверить, не сошел ли он с ума от нашей подмены
GC.Collect();
GC.WaitForPendingFinalizers();
// Assert
Assert.Equal(102, morphedList.Count);
Assert.Equal(NormalValue1, morphedList[0]);
Assert.Equal(10 * 1.1, morphedList[12], 5); // Проверка случайного элемента
}
#endregion
#region 3. Слабое место: Ловушка `ArrayTypeMismatchException` при добавлении в WrapAsList
// WrapAsList создает фейковый список, подменяя MethodTable исходного массива на double[].
// Но когда мы вызываем List.Add(), рантайм внутри делает Array.Copy().
// Если CLR поймет, что исходный массив физически был массивом структур Length,
// вылетит ArrayTypeMismatchException.
[Fact]
public void WrapAsList_Must_Allow_Adding_Elements_Without_ArrayTypeMismatchException()
{
// Arrange
Length[] sourceArray = [(Length)NormalValue1, (Length)NormalValue2];
// Act
List<double> wrappedList = sourceArray.WrapAsList();
// Assert
// Это действие вызывает внутренний Array.Copy. Самое хрупкое место!
var exception = Record.Exception(() => wrappedList.Add(999.99));
Assert.Null(exception); // Тест провален, если здесь вылетит исключение типа массива
Assert.Equal(3, wrappedList.Count);
Assert.Equal(999.99, wrappedList[2]);
}
#endregion
#region 4. Слабое место: Выравнивание памяти и макет Nullable типов
// Структура Nullable<T> в памяти имеет размер больше, чем T (из-за флага HasValue и выравнивания).
// Подмена MethodTable для Nullable<Length>[] в Nullable<double>[] — это огромный риск
// смещения байт. Проверяем, что null остается null, а значения не затираются.
[Fact]
public void Nullable_Array_ReCast_Should_Not_Corrupt_Flags_And_Values()
{
// Arrange
Length?[] source = [(Length)NormalValue1, null, (Length)NormalValue2, null];
// Act
double?[] morphed = source.ReCast();
// Assert
Assert.Equal(source.Length, morphed.Length);
Assert.Equal(NormalValue1, morphed[0]);
Assert.Null(morphed[1]);
Assert.Equal(NormalValue2, morphed[2]);
Assert.Null(morphed[3]);
}
#endregion
#region 5. Слабое место: In-Place мутация (Побочный эффект "Вуду")
// Так как ReCast и WrapAsList меняют MethodTable *оригинального* объекта прямо в куче,
// старая ссылка на массив Length[] теперь указывает на объект, который думает, что он double[].
// Проверяем, как ведет себя оригинальная переменная после этого хака.
[Fact]
public void Verify_InPlace_Mutation_SideEffect_Does_Not_Crash_Old_Reference()
{
// Arrange
Length[] originalArray = [(Length)NormalValue1];
// Act
double[] morphedArray = originalArray.ReCast();
// Изменяем элемент через НОВЫЙ массив
morphedArray[0] = 777.77;
// Assert
// Внимание: из-за жесткой мутации в куче originalArray[0] теперь ТОЖЕ вернет 777.77,
// потому что они смотрят на одну память. Главное — чтобы CLR не упал при обращении к старой ссылке.
double valueFromOldRef = (double)originalArray[0];
Assert.Equal(777.77, valueFromOldRef);
Assert.Same(originalArray, morphedArray); // Физически это один и тот же объект в памяти
}
#endregion
#region 6. Слабое место: Фильтры CLR и оптимизации LINQ
// Компилятор и JIT часто оптимизируют методы вроде .Select() или .ToArray(),
// опираясь на тип MethodTable. Проверяем, съедят ли механизмы LINQ наш "поддельный" тип.
[Fact]
public void Morphed_Array_Should_Pass_Through_Linq_And_Native_Sorting_Validations()
{
// Arrange
Length[] originalArray = [(Length)NormalValue2, (Length)NormalValue1]; // [100.05, 42.42]
// Act
double[] morphedArray = originalArray.ReCast();
// 1. Проверка LINQ фильтрации
double[] processedViaLinq = [.. morphedArray.Where(x => x > 50.0)];
Assert.Single(processedViaLinq);
Assert.Equal(NormalValue2, processedViaLinq[0]);
// 2. Проверка работы встроенной нативной сортировки (Array.Sort использует JIT-оптимизации)
var sortException = Record.Exception(() => Array.Sort(morphedArray));
Assert.Null(sortException);
Assert.True(morphedArray[0] < morphedArray[1]); // Теперь [42.42, 100.05]
}
#endregion
}

View File

@@ -0,0 +1,292 @@
namespace QWERTYkez.Mensura.Tests
{
public class CollectionsDivideExtensionsTests
{
private const double Tolerance = 1e-12;
private static readonly Length scalarUnit = Length.Meter; // 1000 mm
private static readonly double scalarDouble = 2.0;
// Коллекции для тестирования: два элемента
private static readonly Length[] unitsArray = [Length.Meter, Length._MilliMeter];
private static readonly Length?[] nullableUnitsArray = [Length.Meter, null, Length._MilliMeter];
private static readonly List<Length> unitsList = [Length.Meter, Length._MilliMeter];
private static readonly List<Length?> nullableUnitsList = [Length.Meter, null, Length._MilliMeter];
private static readonly double[] doubleArray = [2.0, 3.0];
private static readonly double?[] nullableDoubleArray = [2.0, null, 3.0];
// ====================== 1. T[] / double ======================
[Fact]
public void Div_TArray_Double_Returns_TArray()
{
var result = unitsArray.Div<Length>(scalarDouble);
Assert.Equal(2, result.Length);
Assert.Equal(1000 / 2.0, (double)result[0], Tolerance);
Assert.Equal(1 / 2.0, (double)result[1], Tolerance);
}
// ====================== 2. T?[] / double ======================
[Fact]
public void Div_NullableTArray_Double_Returns_NullableTArray()
{
var result = nullableUnitsArray.Div<Length>(scalarDouble);
Assert.Equal(3, result.Length);
Assert.Equal(1000 / 2.0, (double)result[0]!, Tolerance);
Assert.Null(result[1]);
Assert.Equal(1 / 2.0, (double)result[2]!, Tolerance);
}
// ====================== 3. double / T[] ======================
[Fact]
public void Div_Double_TArray_Returns_TArray()
{
var result = scalarDouble.Div<Length>(unitsArray);
Assert.Equal(2, result.Length);
Assert.Equal(2.0 / 1000, (double)result[0], Tolerance);
Assert.Equal(2.0 / 1, (double)result[1], Tolerance);
}
// ====================== 4. double / T?[] ======================
[Fact]
public void Div_Double_NullableTArray_Returns_NullableTArray()
{
var result = scalarDouble.Div<Length>(nullableUnitsArray);
Assert.Equal(3, result.Length);
Assert.Equal(2.0 / 1000, (double)result[0]!, Tolerance);
Assert.Null(result[1]);
Assert.Equal(2.0 / 1, (double)result[2]!, Tolerance);
}
// ====================== 5. List<T> / double ======================
[Fact]
public void Div_ListT_Double_Returns_ListT()
{
var result = unitsList.Div<Length>(scalarDouble);
Assert.Equal(2, result.Count);
Assert.Equal(1000 / 2.0, (double)result[0], Tolerance);
Assert.Equal(1 / 2.0, (double)result[1], Tolerance);
}
// ====================== 6. List<T?> / double ======================
[Fact]
public void Div_ListNullableT_Double_Returns_ListNullableT()
{
var result = nullableUnitsList.Div<Length>(scalarDouble);
Assert.Equal(3, result.Count);
Assert.Equal(1000 / 2.0, (double)result[0]!, Tolerance);
Assert.Null(result[1]);
Assert.Equal(1 / 2.0, (double)result[2]!, Tolerance);
}
// ====================== 7. double / List<T> ======================
[Fact]
public void Div_Double_ListT_Returns_ListT()
{
var result = scalarDouble.Div<Length>(unitsList);
Assert.Equal(2, result.Count);
Assert.Equal(2.0 / 1000, (double)result[0], Tolerance);
Assert.Equal(2.0 / 1, (double)result[1], Tolerance);
}
// ====================== 8. double / List<T?> ======================
[Fact]
public void Div_Double_ListNullableT_Returns_ListNullableT()
{
var result = scalarDouble.Div<Length>(nullableUnitsList);
Assert.Equal(3, result.Count);
Assert.Equal(2.0 / 1000, (double)result[0]!, Tolerance);
Assert.Null(result[1]);
Assert.Equal(2.0 / 1, (double)result[2]!, Tolerance);
}
// ====================== 9. IReadOnlyCollection<T> / double (Span) ======================
[Fact]
public void Div_IReadOnlyCollectionT_Double_Span()
{
Span<Length> dest = new Length[2];
((IReadOnlyCollection<Length>)unitsArray).Div(scalarDouble, dest);
Assert.Equal(1000 / 2.0, (double)dest[0], Tolerance);
Assert.Equal(1 / 2.0, (double)dest[1], Tolerance);
}
// ====================== 10. IReadOnlyCollection<T?> / double (Span) ======================
[Fact]
public void Div_IReadOnlyCollectionNullableT_Double_Span()
{
Span<Length?> dest = new Length?[3];
((IReadOnlyCollection<Length?>)nullableUnitsArray).Div(scalarDouble, dest);
Assert.Equal(1000 / 2.0, (double)dest[0]!, Tolerance);
Assert.Null(dest[1]);
Assert.Equal(1 / 2.0, (double)dest[2]!, Tolerance);
}
// ====================== 11. double / IReadOnlyCollection<T> (Span) ======================
[Fact]
public void Div_Double_IReadOnlyCollectionT_Span()
{
Span<Length> dest = new Length[2];
scalarDouble.Div((IReadOnlyCollection<Length>)unitsArray, dest);
Assert.Equal(2.0 / 1000, (double)dest[0], Tolerance);
Assert.Equal(2.0 / 1, (double)dest[1], Tolerance);
}
// ====================== 12. double / IReadOnlyCollection<T?> (Span) ======================
[Fact]
public void Div_Double_IReadOnlyCollectionNullableT_Span()
{
Span<Length?> dest = new Length?[3];
scalarDouble.Div((IReadOnlyCollection<Length?>)nullableUnitsArray, dest);
Assert.Equal(2.0 / 1000, (double)dest[0]!, Tolerance);
Assert.Null(dest[1]);
Assert.Equal(2.0 / 1, (double)dest[2]!, Tolerance);
}
// ====================== 13. IEnumerable<T> / double ======================
[Fact]
public void Div_IEnumerableT_Double_Returns_IEnumerableT()
{
var result = ((IEnumerable<Length>)unitsArray).Div<Length>(scalarDouble).ToList();
Assert.Equal(2, result.Count);
Assert.Equal(1000 / 2.0, (double)result[0], Tolerance);
Assert.Equal(1 / 2.0, (double)result[1], Tolerance);
}
// ====================== 14. IEnumerable<T?> / double ======================
[Fact]
public void Div_IEnumerableNullableT_Double_Returns_IEnumerableNullableT()
{
var result = ((IEnumerable<Length?>)nullableUnitsArray).Div<Length>(scalarDouble).ToList();
Assert.Equal(3, result.Count);
Assert.Equal(1000 / 2.0, (double)result[0]!, Tolerance);
Assert.Null(result[1]);
Assert.Equal(1 / 2.0, (double)result[2]!, Tolerance);
}
// ====================== 15. double / IEnumerable<T> ======================
[Fact]
public void Div_Double_IEnumerableT_Returns_IEnumerableT()
{
var result = scalarDouble.Div<Length>((IEnumerable<Length>)unitsArray).ToList();
Assert.Equal(2, result.Count);
Assert.Equal(2.0 / 1000, (double)result[0], Tolerance);
Assert.Equal(2.0 / 1, (double)result[1], Tolerance);
}
// ====================== 16. double / IEnumerable<T?> ======================
[Fact]
public void Div_Double_IEnumerableNullableT_Returns_IEnumerableNullableT()
{
var result = scalarDouble.Div<Length>((IEnumerable<Length?>)nullableUnitsArray).ToList();
Assert.Equal(3, result.Count);
Assert.Equal(2.0 / 1000, (double)result[0]!, Tolerance);
Assert.Null(result[1]);
Assert.Equal(2.0 / 1, (double)result[2]!, Tolerance);
}
// ====================== 17. double[] / T ======================
[Fact]
public void Div_DoubleArray_T_Returns_TArray()
{
var result = doubleArray.Div(scalarUnit);
Assert.Equal(2, result.Length);
Assert.Equal(2.0 / 1000, (double)result[0], Tolerance);
Assert.Equal(3.0 / 1000, (double)result[1], Tolerance);
}
// ====================== 18. double?[] / T ======================
[Fact]
public void Div_NullableDoubleArray_T_Returns_NullableTArray()
{
var result = nullableDoubleArray.Div(scalarUnit);
Assert.Equal(3, result.Length);
Assert.Equal(2.0 / 1000, (double)result[0]!, Tolerance);
Assert.Null(result[1]);
Assert.Equal(3.0 / 1000, (double)result[2]!, Tolerance);
}
// ====================== 19. T / double[] ======================
[Fact]
public void Div_T_DoubleArray_Returns_TArray()
{
var result = scalarUnit.Div(doubleArray);
Assert.Equal(2, result.Length);
Assert.Equal(1000 / 2.0, (double)result[0], Tolerance);
Assert.Equal(1000 / 3.0, (double)result[1], Tolerance);
}
// ====================== 20. T / double?[] ======================
[Fact]
public void Div_T_NullableDoubleArray_Returns_NullableTArray()
{
var result = scalarUnit.Div(nullableDoubleArray);
Assert.Equal(3, result.Length);
Assert.Equal(1000 / 2.0, (double)result[0]!, Tolerance);
Assert.Null(result[1]);
Assert.Equal(1000 / 3.0, (double)result[2]!, Tolerance);
}
// ====================== 21. List<double> / T ======================
[Fact]
public void Div_ListDouble_T_Returns_ListT()
{
var list = new List<double> { 2.0, 3.0 };
var result = list.Div(scalarUnit);
Assert.Equal(2, result.Count);
Assert.Equal(2.0 / 1000, (double)result[0], Tolerance);
Assert.Equal(3.0 / 1000, (double)result[1], Tolerance);
}
// ====================== 22. List<double?> / T ======================
[Fact]
public void Div_ListNullableDouble_T_Returns_ListNullableT()
{
var list = new List<double?> { 2.0, null, 3.0 };
var result = list.Div(scalarUnit);
Assert.Equal(3, result.Count);
Assert.Equal(2.0 / 1000, (double)result[0]!, Tolerance);
Assert.Null(result[1]);
Assert.Equal(3.0 / 1000, (double)result[2]!, Tolerance);
}
// ====================== 23. T / List<double> ======================
[Fact]
public void Div_T_ListDouble_Returns_ListT()
{
var list = new List<double> { 2.0, 3.0 };
var result = scalarUnit.Div(list);
Assert.Equal(2, result.Count);
Assert.Equal(1000 / 2.0, (double)result[0], Tolerance);
Assert.Equal(1000 / 3.0, (double)result[1], Tolerance);
}
// ====================== 24. T / List<double?> ======================
[Fact]
public void Div_T_ListNullableDouble_Returns_ListNullableT()
{
var list = new List<double?> { 2.0, null, 3.0 };
var result = scalarUnit.Div(list);
Assert.Equal(3, result.Count);
Assert.Equal(1000 / 2.0, (double)result[0]!, Tolerance);
Assert.Null(result[1]);
Assert.Equal(1000 / 3.0, (double)result[2]!, Tolerance);
}
// ====================== 25. T / T[] -> double[] ======================
[Fact]
public void Div_T_TArray_Returns_DoubleArray()
{
var result = scalarUnit.Div(unitsArray);
Assert.Equal(2, result.Length);
Assert.Equal(1000 / 1000.0, result[0], Tolerance);
Assert.Equal(1000 / 1.0, result[1], Tolerance);
}
// ====================== Дополнительно: пустые коллекции, null аргументы ======================
[Fact]
public void Div_EmptyArray_ReturnsEmptyArray()
{
var empty = Array.Empty<Length>();
var result = empty.Div<Length>(2.0);
Assert.Empty(result);
}
}
}

View File

@@ -0,0 +1,337 @@
namespace QWERTYkez.Mensura.Tests
{
public class CollectionsMinusExtensionsTests
{
private const double Tolerance = 1e-12;
private static readonly Length scalarUnit = Length.Meter; // 1000 mm
private static readonly double scalarDouble = 1000.0; // уменьшаемое/вычитаемое в мм
// Коллекции единиц
private static readonly Length[] unitsArray = [Length.Meter, Length._MilliMeter];
private static readonly Length?[] nullableUnitsArray = [Length.Meter, null, Length._MilliMeter];
private static readonly List<Length> unitsList = [Length.Meter, Length._MilliMeter];
private static readonly List<Length?> nullableUnitsList = [Length.Meter, null, Length._MilliMeter];
// Коллекции double
private static readonly double[] doubleArray = [500.0, 200.0]; // мм
private static readonly double?[] nullableDoubleArray = [500.0, null, 200.0];
private static readonly List<double> doubleList = [500.0, 200.0];
private static readonly List<double?> nullableDoubleList = [500.0, null, 200.0];
// ====================== 1. T[] - double (результат T[]) ======================
[Fact]
public void Minus_TArray_Double_Returns_TArray()
{
// units - subtrahend
var result = unitsArray.Minus<Length>(scalarDouble);
Assert.Equal(2, result.Length);
Assert.Equal(1000 - 1000, (double)result[0], Tolerance);
Assert.Equal(1 - 1000, (double)result[1], Tolerance);
}
[Fact]
public void Minus_TArray_Double_ByZero_Works()
{
var result = unitsArray.Minus<Length>(0.0);
Assert.Equal(1000, (double)result[0], Tolerance);
Assert.Equal(1, (double)result[1], Tolerance);
}
// ====================== 2. T?[] - double (результат T?[]) ======================
[Fact]
public void Minus_NullableTArray_Double_Returns_NullableTArray()
{
var result = nullableUnitsArray.Minus<Length>(scalarDouble);
Assert.Equal(3, result.Length);
Assert.Equal(1000 - 1000, (double)result[0]!, Tolerance);
Assert.Null(result[1]);
Assert.Equal(1 - 1000, (double)result[2]!, Tolerance);
}
// ====================== 3. double - T[] (результат T[]) ======================
[Fact]
public void Minus_Double_TArray_Returns_TArray()
{
var result = scalarDouble.Minus<Length>(unitsArray);
Assert.Equal(2, result.Length);
Assert.Equal(1000 - 1000, (double)result[0], Tolerance);
Assert.Equal(1000 - 1, (double)result[1], Tolerance);
}
// ====================== 4. double - T?[] (результат T?[]) ======================
[Fact]
public void Minus_Double_NullableTArray_Returns_NullableTArray()
{
var result = scalarDouble.Minus<Length>(nullableUnitsArray);
Assert.Equal(3, result.Length);
Assert.Equal(1000 - 1000, (double)result[0]!, Tolerance);
Assert.Null(result[1]);
Assert.Equal(1000 - 1, (double)result[2]!, Tolerance);
}
// ====================== 5. List<T> - double ======================
[Fact]
public void Minus_ListT_Double_Returns_ListT()
{
var result = unitsList.Minus<Length>(scalarDouble);
Assert.Equal(2, result.Count);
Assert.Equal(1000 - 1000, (double)result[0], Tolerance);
Assert.Equal(1 - 1000, (double)result[1], Tolerance);
}
// ====================== 6. List<T?> - double ======================
[Fact]
public void Minus_ListNullableT_Double_Returns_ListNullableT()
{
var result = nullableUnitsList.Minus<Length>(scalarDouble);
Assert.Equal(3, result.Count);
Assert.Equal(1000 - 1000, (double)result[0]!, Tolerance);
Assert.Null(result[1]);
Assert.Equal(1 - 1000, (double)result[2]!, Tolerance);
}
// ====================== 7. double - List<T> ======================
[Fact]
public void Minus_Double_ListT_Returns_ListT()
{
var result = scalarDouble.Minus<Length>(unitsList);
Assert.Equal(2, result.Count);
Assert.Equal(1000 - 1000, (double)result[0], Tolerance);
Assert.Equal(1000 - 1, (double)result[1], Tolerance);
}
// ====================== 8. double - List<T?> ======================
[Fact]
public void Minus_Double_ListNullableT_Returns_ListNullableT()
{
var result = scalarDouble.Minus<Length>(nullableUnitsList);
Assert.Equal(3, result.Count);
Assert.Equal(1000 - 1000, (double)result[0]!, Tolerance);
Assert.Null(result[1]);
Assert.Equal(1000 - 1, (double)result[2]!, Tolerance);
}
// ====================== 9. IReadOnlyCollection<T> - double (Span) ======================
[Fact]
public void Minus_IReadOnlyCollectionT_Double_Span()
{
Span<Length> dest = new Length[2];
((IReadOnlyCollection<Length>)unitsArray).Minus<Length>(scalarDouble, dest);
Assert.Equal(1000 - 1000, (double)dest[0], Tolerance);
Assert.Equal(1 - 1000, (double)dest[1], Tolerance);
}
// ====================== 10. IReadOnlyCollection<T?> - double (Span) ======================
[Fact]
public void Minus_IReadOnlyCollectionNullableT_Double_Span()
{
Span<Length?> dest = new Length?[3];
((IReadOnlyCollection<Length?>)nullableUnitsArray).Minus<Length>(scalarDouble, dest);
Assert.Equal(1000 - 1000, (double)dest[0]!, Tolerance);
Assert.Null(dest[1]);
Assert.Equal(1 - 1000, (double)dest[2]!, Tolerance);
}
// ====================== 11. double - IReadOnlyCollection<T> (Span) ======================
[Fact]
public void Minus_Double_IReadOnlyCollectionT_Span()
{
Span<Length> dest = new Length[2];
scalarDouble.Minus<Length>((IReadOnlyCollection<Length>)unitsArray, dest);
Assert.Equal(1000 - 1000, (double)dest[0], Tolerance);
Assert.Equal(1000 - 1, (double)dest[1], Tolerance);
}
// ====================== 12. double - IReadOnlyCollection<T?> (Span) ======================
[Fact]
public void Minus_Double_IReadOnlyCollectionNullableT_Span()
{
Span<Length?> dest = new Length?[3];
scalarDouble.Minus<Length>((IReadOnlyCollection<Length?>)nullableUnitsArray, dest);
Assert.Equal(1000 - 1000, (double)dest[0]!, Tolerance);
Assert.Null(dest[1]);
Assert.Equal(1000 - 1, (double)dest[2]!, Tolerance);
}
// ====================== 13. IEnumerable<T> - double ======================
[Fact]
public void Minus_IEnumerableT_Double_Returns_IEnumerableT()
{
var result = ((IEnumerable<Length>)unitsArray).Minus<Length>(scalarDouble).ToList();
Assert.Equal(2, result.Count);
Assert.Equal(1000 - 1000, (double)result[0], Tolerance);
Assert.Equal(1 - 1000, (double)result[1], Tolerance);
}
// ====================== 14. IEnumerable<T?> - double ======================
[Fact]
public void Minus_IEnumerableNullableT_Double_Returns_IEnumerableNullableT()
{
var result = ((IEnumerable<Length?>)nullableUnitsArray).Minus<Length>(scalarDouble).ToList();
Assert.Equal(3, result.Count);
Assert.Equal(1000 - 1000, (double)result[0]!, Tolerance);
Assert.Null(result[1]);
Assert.Equal(1 - 1000, (double)result[2]!, Tolerance);
}
// ====================== 15. double - IEnumerable<T> ======================
[Fact]
public void Minus_Double_IEnumerableT_Returns_IEnumerableT()
{
var result = scalarDouble.Minus<Length>((IEnumerable<Length>)unitsArray).ToList();
Assert.Equal(2, result.Count);
Assert.Equal(1000 - 1000, (double)result[0], Tolerance);
Assert.Equal(1000 - 1, (double)result[1], Tolerance);
}
// ====================== 16. double - IEnumerable<T?> ======================
[Fact]
public void Minus_Double_IEnumerableNullableT_Returns_IEnumerableNullableT()
{
var result = scalarDouble.Minus<Length>((IEnumerable<Length?>)nullableUnitsArray).ToList();
Assert.Equal(3, result.Count);
Assert.Equal(1000 - 1000, (double)result[0]!, Tolerance);
Assert.Null(result[1]);
Assert.Equal(1000 - 1, (double)result[2]!, Tolerance);
}
// ====================== 17. double[] - T ======================
[Fact]
public void Minus_DoubleArray_T_Returns_TArray()
{
var result = doubleArray.Minus(scalarUnit);
Assert.Equal(2, result.Length);
Assert.Equal(500 - 1000, (double)result[0], Tolerance);
Assert.Equal(200 - 1000, (double)result[1], Tolerance);
}
// ====================== 18. double?[] - T ======================
[Fact]
public void Minus_NullableDoubleArray_T_Returns_NullableTArray()
{
var result = nullableDoubleArray.Minus(scalarUnit);
Assert.Equal(3, result.Length);
Assert.Equal(500 - 1000, (double)result[0]!, Tolerance);
Assert.Null(result[1]);
Assert.Equal(200 - 1000, (double)result[2]!, Tolerance);
}
// ====================== 19. T - double[] ======================
[Fact]
public void Minus_T_DoubleArray_Returns_TArray()
{
var result = scalarUnit.Minus(doubleArray);
Assert.Equal(2, result.Length);
Assert.Equal(1000 - 500, (double)result[0], Tolerance);
Assert.Equal(1000 - 200, (double)result[1], Tolerance);
}
// ====================== 20. T - double?[] ======================
[Fact]
public void Minus_T_NullableDoubleArray_Returns_NullableTArray()
{
var result = scalarUnit.Minus(nullableDoubleArray);
Assert.Equal(3, result.Length);
Assert.Equal(1000 - 500, (double)result[0]!, Tolerance);
Assert.Null(result[1]);
Assert.Equal(1000 - 200, (double)result[2]!, Tolerance);
}
// ====================== 21. List<double> - T ======================
[Fact]
public void Minus_ListDouble_T_Returns_ListT()
{
var result = doubleList.Minus(scalarUnit);
Assert.Equal(2, result.Count);
Assert.Equal(500 - 1000, (double)result[0], Tolerance);
Assert.Equal(200 - 1000, (double)result[1], Tolerance);
}
// ====================== 22. List<double?> - T ======================
[Fact]
public void Minus_ListNullableDouble_T_Returns_ListNullableT()
{
var result = nullableDoubleList.Minus(scalarUnit);
Assert.Equal(3, result.Count);
Assert.Equal(500 - 1000, (double)result[0]!, Tolerance);
Assert.Null(result[1]);
Assert.Equal(200 - 1000, (double)result[2]!, Tolerance);
}
// ====================== 23. T - List<double> ======================
[Fact]
public void Minus_T_ListDouble_Returns_ListT()
{
var result = scalarUnit.Minus(doubleList);
Assert.Equal(2, result.Count);
Assert.Equal(1000 - 500, (double)result[0], Tolerance);
Assert.Equal(1000 - 200, (double)result[1], Tolerance);
}
// ====================== 24. T - List<double?> ======================
[Fact]
public void Minus_T_ListNullableDouble_Returns_ListNullableT()
{
var result = scalarUnit.Minus(nullableDoubleList);
Assert.Equal(3, result.Count);
Assert.Equal(1000 - 500, (double)result[0]!, Tolerance);
Assert.Null(result[1]);
Assert.Equal(1000 - 200, (double)result[2]!, Tolerance);
}
// ====================== 25. T - T[] -> double[] ======================
[Fact]
public void Minus_T_TArray_Returns_DoubleArray()
{
var result = scalarUnit - unitsArray;
Assert.Equal(2, result.Length);
Assert.Equal(1000 - 1000, result[0]._Value, Tolerance);
Assert.Equal(1000 - 1, result[1]._Value, Tolerance);
}
// Дополнительно: перегрузки, где результат double (без указания R)
// Тестируем Minus<T>(T[] units, T subtrahend) возвращает double[]
[Fact]
public void Minus_TArray_T_Returns_DoubleArray()
{
var result = unitsArray - scalarUnit;
Assert.Equal(2, result.Length);
Assert.Equal(1000 - 1000, result[0]._Value, Tolerance);
Assert.Equal(1 - 1000, result[1]._Value, Tolerance);
}
[Fact]
public void Minus_ListT_T_Returns_DoubleList()
{
var result = unitsList - scalarUnit;
Assert.Equal(2, result.Count);
Assert.Equal(1000 - 1000, result[0]._Value, Tolerance);
Assert.Equal(1 - 1000, result[1]._Value, Tolerance);
}
// Проверка пустых коллекций
[Fact]
public void Minus_EmptyArray_ReturnsEmptyArray()
{
var empty = Array.Empty<Length>();
var result = empty.Minus<Length>(5.0);
Assert.Empty(result);
}
[Fact]
public void Minus_NullArray_ReturnsNull()
{
Length[] nullArray = null!;
var result = nullArray.Minus<Length>(5.0);
Assert.Null(result);
}
[Fact]
public void Minus_NullList_ReturnsNull()
{
List<Length> nullList = null!;
var result = nullList.Minus<Length>(5.0);
Assert.Null(result);
}
}
}

View File

@@ -0,0 +1,335 @@
namespace QWERTYkez.Mensura.Tests
{
public class CollectionsMultiplyExtensionsTests
{
private const double Tolerance = 1e-12;
private static readonly Length scalarUnit = Length.Meter; // 1000 mm
private static readonly double scalarDouble = 2.0;
// Коллекции единиц
private static readonly Length[] unitsArray = [Length.Meter, Length._MilliMeter];
private static readonly Length?[] nullableUnitsArray = [Length.Meter, null, Length._MilliMeter];
private static readonly List<Length> unitsList = [Length.Meter, Length._MilliMeter];
private static readonly List<Length?> nullableUnitsList = [Length.Meter, null, Length._MilliMeter];
// Коллекции double
private static readonly double[] doubleArray = [5.0, 3.0];
private static readonly double?[] nullableDoubleArray = [5.0, null, 3.0];
private static readonly List<double> doubleList = [5.0, 3.0];
private static readonly List<double?> nullableDoubleList = [5.0, null, 3.0];
// ====================== 1. T[] * double ======================
[Fact]
public void Mul_TArray_Double_Returns_TArray()
{
var result = unitsArray.Mul<Length>(scalarDouble);
Assert.Equal(2, result.Length);
Assert.Equal(1000 * 2, (double)result[0], Tolerance);
Assert.Equal(1 * 2, (double)result[1], Tolerance);
}
[Fact]
public void Mul_TArray_Double_ByZero_Works()
{
var result = unitsArray.Mul<Length>(0.0);
Assert.Equal(0, (double)result[0], Tolerance);
Assert.Equal(0, (double)result[1], Tolerance);
}
// ====================== 2. T?[] * double ======================
[Fact]
public void Mul_NullableTArray_Double_Returns_NullableTArray()
{
var result = nullableUnitsArray.Mul<Length>(scalarDouble);
Assert.Equal(3, result.Length);
Assert.Equal(1000 * 2, (double)result[0]!, Tolerance);
Assert.Null(result[1]); // null * 2 = null
Assert.Equal(1 * 2, (double)result[2]!, Tolerance);
}
// ====================== 3. double * T[] ======================
[Fact]
public void Mul_Double_TArray_Returns_TArray()
{
var result = scalarDouble.Mul<Length>(unitsArray);
Assert.Equal(2, result.Length);
Assert.Equal(2 * 1000, (double)result[0], Tolerance);
Assert.Equal(2 * 1, (double)result[1], Tolerance);
}
// ====================== 4. double * T?[] ======================
[Fact]
public void Mul_Double_NullableTArray_Returns_NullableTArray()
{
var result = scalarDouble.Mul<Length>(nullableUnitsArray);
Assert.Equal(3, result.Length);
Assert.Equal(2 * 1000, (double)result[0]!, Tolerance);
Assert.Null(result[1]); // 2 * null = null
Assert.Equal(2 * 1, (double)result[2]!, Tolerance);
}
// ====================== 5. List<T> * double ======================
[Fact]
public void Mul_ListT_Double_Returns_ListT()
{
var result = unitsList.Mul<Length>(scalarDouble);
Assert.Equal(2, result.Count);
Assert.Equal(1000 * 2, (double)result[0], Tolerance);
Assert.Equal(1 * 2, (double)result[1], Tolerance);
}
// ====================== 6. List<T?> * double ======================
[Fact]
public void Mul_ListNullableT_Double_Returns_ListNullableT()
{
var result = nullableUnitsList.Mul<Length>(scalarDouble);
Assert.Equal(3, result.Count);
Assert.Equal(1000 * 2, (double)result[0]!, Tolerance);
Assert.Null(result[1]); // null * 2 = null
Assert.Equal(1 * 2, (double)result[2]!, Tolerance);
}
// ====================== 7. double * List<T> ======================
[Fact]
public void Mul_Double_ListT_Returns_ListT()
{
var result = scalarDouble.Mul<Length>(unitsList);
Assert.Equal(2, result.Count);
Assert.Equal(2 * 1000, (double)result[0], Tolerance);
Assert.Equal(2 * 1, (double)result[1], Tolerance);
}
// ====================== 8. double * List<T?> ======================
[Fact]
public void Mul_Double_ListNullableT_Returns_ListNullableT()
{
var result = scalarDouble.Mul<Length>(nullableUnitsList);
Assert.Equal(3, result.Count);
Assert.Equal(2 * 1000, (double)result[0]!, Tolerance);
Assert.Null(result[1]); // 2 * null = null
Assert.Equal(2 * 1, (double)result[2]!, Tolerance);
}
// ====================== 9. IReadOnlyCollection<T> * double (Span) ======================
[Fact]
public void Mul_IReadOnlyCollectionT_Double_Span()
{
Span<Length> dest = new Length[2];
((IReadOnlyCollection<Length>)unitsArray).Mul<Length>(scalarDouble, dest);
Assert.Equal(1000 * 2, (double)dest[0], Tolerance);
Assert.Equal(1 * 2, (double)dest[1], Tolerance);
}
// ====================== 10. IReadOnlyCollection<T?> * double (Span) ======================
[Fact]
public void Mul_IReadOnlyCollectionNullableT_Double_Span()
{
Span<Length?> dest = new Length?[3];
((IReadOnlyCollection<Length?>)nullableUnitsArray).Mul<Length>(scalarDouble, dest);
Assert.Equal(1000 * 2, (double)dest[0]!, Tolerance);
Assert.Null(dest[1]); // null * 2 = null
Assert.Equal(1 * 2, (double)dest[2]!, Tolerance);
}
// ====================== 11. double * IReadOnlyCollection<T> (Span) ======================
[Fact]
public void Mul_Double_IReadOnlyCollectionT_Span()
{
Span<Length> dest = new Length[2];
scalarDouble.Mul<Length>((IReadOnlyCollection<Length>)unitsArray, dest);
Assert.Equal(2 * 1000, (double)dest[0], Tolerance);
Assert.Equal(2 * 1, (double)dest[1], Tolerance);
}
// ====================== 12. double * IReadOnlyCollection<T?> (Span) ======================
[Fact]
public void Mul_Double_IReadOnlyCollectionNullableT_Span()
{
Span<Length?> dest = new Length?[3];
scalarDouble.Mul<Length>((IReadOnlyCollection<Length?>)nullableUnitsArray, dest);
Assert.Equal(2 * 1000, (double)dest[0]!, Tolerance);
Assert.Null(dest[1]); // 2 * null = null
Assert.Equal(2 * 1, (double)dest[2]!, Tolerance);
}
// ====================== 13. IEnumerable<T> * double ======================
[Fact]
public void Mul_IEnumerableT_Double_Returns_IEnumerableT()
{
var result = ((IEnumerable<Length>)unitsArray).Mul<Length>(scalarDouble).ToList();
Assert.Equal(2, result.Count);
Assert.Equal(1000 * 2, (double)result[0], Tolerance);
Assert.Equal(1 * 2, (double)result[1], Tolerance);
}
// ====================== 14. IEnumerable<T?> * double ======================
[Fact]
public void Mul_IEnumerableNullableT_Double_Returns_IEnumerableNullableT()
{
var result = ((IEnumerable<Length?>)nullableUnitsArray).Mul<Length>(scalarDouble).ToList();
Assert.Equal(3, result.Count);
Assert.Equal(1000 * 2, (double)result[0]!, Tolerance);
Assert.Null(result[1]); // null * 2 = null
Assert.Equal(1 * 2, (double)result[2]!, Tolerance);
}
// ====================== 15. double * IEnumerable<T> ======================
[Fact]
public void Mul_Double_IEnumerableT_Returns_IEnumerableT()
{
var result = scalarDouble.Mul<Length>((IEnumerable<Length>)unitsArray).ToList();
Assert.Equal(2, result.Count);
Assert.Equal(2 * 1000, (double)result[0], Tolerance);
Assert.Equal(2 * 1, (double)result[1], Tolerance);
}
// ====================== 16. double * IEnumerable<T?> ======================
[Fact]
public void Mul_Double_IEnumerableNullableT_Returns_IEnumerableNullableT()
{
var result = scalarDouble.Mul<Length>((IEnumerable<Length?>)nullableUnitsArray).ToList();
Assert.Equal(3, result.Count);
Assert.Equal(2 * 1000, (double)result[0]!, Tolerance);
Assert.Null(result[1]); // 2 * null = null
Assert.Equal(2 * 1, (double)result[2]!, Tolerance);
}
// ====================== 17. double[] * T ======================
[Fact]
public void Mul_DoubleArray_T_Returns_TArray()
{
var result = doubleArray.Mul(scalarUnit);
Assert.Equal(2, result.Length);
Assert.Equal(5 * 1000, (double)result[0], Tolerance);
Assert.Equal(3 * 1000, (double)result[1], Tolerance);
}
// ====================== 18. double?[] * T ======================
[Fact]
public void Mul_NullableDoubleArray_T_Returns_NullableTArray()
{
var result = nullableDoubleArray.Mul(scalarUnit);
Assert.Equal(3, result.Length);
Assert.Equal(5 * 1000, (double)result[0]!, Tolerance);
Assert.Null(result[1]); // null * Length = null
Assert.Equal(3 * 1000, (double)result[2]!, Tolerance);
}
// ====================== 19. T * double[] ======================
[Fact]
public void Mul_T_DoubleArray_Returns_TArray()
{
var result = scalarUnit.Mul(doubleArray);
Assert.Equal(2, result.Length);
Assert.Equal(1000 * 5, (double)result[0], Tolerance);
Assert.Equal(1000 * 3, (double)result[1], Tolerance);
}
// ====================== 20. T * double?[] ======================
[Fact]
public void Mul_T_NullableDoubleArray_Returns_NullableTArray()
{
var result = scalarUnit.Mul(nullableDoubleArray);
Assert.Equal(3, result.Length);
Assert.Equal(1000 * 5, (double)result[0]!, Tolerance);
Assert.Null(result[1]); // Length * null = null
Assert.Equal(1000 * 3, (double)result[2]!, Tolerance);
}
// ====================== 21. List<double> * T ======================
[Fact]
public void Mul_ListDouble_T_Returns_ListT()
{
var result = doubleList.Mul(scalarUnit);
Assert.Equal(2, result.Count);
Assert.Equal(5 * 1000, (double)result[0], Tolerance);
Assert.Equal(3 * 1000, (double)result[1], Tolerance);
}
// ====================== 22. List<double?> * T ======================
[Fact]
public void Mul_ListNullableDouble_T_Returns_ListNullableT()
{
var result = nullableDoubleList.Mul(scalarUnit);
Assert.Equal(3, result.Count);
Assert.Equal(5 * 1000, (double)result[0]!, Tolerance);
Assert.Null(result[1]); // null * Length = null
Assert.Equal(3 * 1000, (double)result[2]!, Tolerance);
}
// ====================== 23. T * List<double> ======================
[Fact]
public void Mul_T_ListDouble_Returns_ListT()
{
var result = scalarUnit.Mul(doubleList);
Assert.Equal(2, result.Count);
Assert.Equal(1000 * 5, (double)result[0], Tolerance);
Assert.Equal(1000 * 3, (double)result[1], Tolerance);
}
// ====================== 24. T * List<double?> ======================
[Fact]
public void Mul_T_ListNullableDouble_Returns_ListNullableT()
{
var result = scalarUnit.Mul(nullableDoubleList);
Assert.Equal(3, result.Count);
Assert.Equal(1000 * 5, (double)result[0]!, Tolerance);
Assert.Null(result[1]); // Length * null = null
Assert.Equal(1000 * 3, (double)result[2]!, Tolerance);
}
// ====================== 25. T * T[] -> double[] ======================
[Fact]
public void Mul_T_TArray_Returns_DoubleArray()
{
var result = scalarUnit * unitsArray;
Assert.Equal(2, result.Length);
Assert.Equal(1000 * 1000, result[0]._Value, Tolerance);
Assert.Equal(1000 * 1, result[1]._Value, Tolerance);
}
// Дополнительно: перегрузки с результатом double (без R)
[Fact]
public void Mul_TArray_T_Returns_DoubleArray()
{
var result = unitsArray * scalarUnit;
Assert.Equal(2, result.Length);
Assert.Equal(1000 * 1000, result[0]._Value, Tolerance);
Assert.Equal(1 * 1000, result[1]._Value, Tolerance);
}
[Fact]
public void Mul_ListT_T_Returns_DoubleList()
{
var result = unitsList * scalarUnit;
Assert.Equal(2, result.Count);
Assert.Equal(1000 * 1000, result[0]._Value, Tolerance);
Assert.Equal(1 * 1000, result[1]._Value, Tolerance);
}
// ====================== Обработка null коллекций ======================
[Fact]
public void Mul_NullArray_ReturnsNull()
{
Length[] nullArray = null!;
var result = nullArray.Mul<Length>(5.0);
Assert.Null(result);
}
[Fact]
public void Mul_NullList_ReturnsNull()
{
List<Length> nullList = null!;
var result = nullList.Mul<Length>(5.0);
Assert.Null(result);
}
[Fact]
public void Mul_EmptyArray_ReturnsEmptyArray()
{
var empty = Array.Empty<Length>();
var result = empty.Mul<Length>(5.0);
Assert.Empty(result);
}
}
}

View File

@@ -0,0 +1,334 @@
using System;
using System.Collections.Generic;
using System.Linq;
using QWERTYkez.Mensura.Units;
using QWERTYkez.Mensura.Extensions;
using Xunit;
namespace QWERTYkez.Mensura.Tests
{
public class CollectionsPlusExtensionsTests
{
private const double Tolerance = 1e-12;
private static readonly Length scalarUnit = Length.Meter; // 1000 mm
private static readonly double scalarDouble = 500.0; // прибавляемое значение в мм
// Коллекции единиц
private static readonly Length[] unitsArray = new[] { Length.Meter, Length._MilliMeter };
private static readonly Length?[] nullableUnitsArray = new Length?[] { Length.Meter, null, Length._MilliMeter };
private static readonly List<Length> unitsList = new List<Length> { Length.Meter, Length._MilliMeter };
private static readonly List<Length?> nullableUnitsList = new List<Length?> { Length.Meter, null, Length._MilliMeter };
// Коллекции double
private static readonly double[] doubleArray = new double[] { 200.0, 300.0 };
private static readonly double?[] nullableDoubleArray = new double?[] { 200.0, null, 300.0 };
private static readonly List<double> doubleList = new List<double> { 200.0, 300.0 };
private static readonly List<double?> nullableDoubleList = new List<double?> { 200.0, null, 300.0 };
// ====================== 1. T[] + double ======================
[Fact]
public void Plus_TArray_Double_Returns_TArray()
{
var result = unitsArray.Plus<Length>(scalarDouble);
Assert.Equal(2, result.Length);
Assert.Equal(1000 + 500, (double)result[0], Tolerance);
Assert.Equal(1 + 500, (double)result[1], Tolerance);
}
// ====================== 2. T?[] + double ======================
[Fact]
public void Plus_NullableTArray_Double_Returns_NullableTArray()
{
var result = nullableUnitsArray.Plus<Length>(scalarDouble);
Assert.Equal(3, result.Length);
Assert.Equal(1000 + 500, (double)result[0]!, Tolerance);
Assert.Null(result[1]); // null + число = null
Assert.Equal(1 + 500, (double)result[2]!, Tolerance);
}
// ====================== 3. double + T[] ======================
[Fact]
public void Plus_Double_TArray_Returns_TArray()
{
var result = scalarDouble.Plus<Length>(unitsArray);
Assert.Equal(2, result.Length);
Assert.Equal(500 + 1000, (double)result[0], Tolerance);
Assert.Equal(500 + 1, (double)result[1], Tolerance);
}
// ====================== 4. double + T?[] ======================
[Fact]
public void Plus_Double_NullableTArray_Returns_NullableTArray()
{
var result = scalarDouble.Plus<Length>(nullableUnitsArray);
Assert.Equal(3, result.Length);
Assert.Equal(500 + 1000, (double)result[0]!, Tolerance);
Assert.Null(result[1]); // число + null = null
Assert.Equal(500 + 1, (double)result[2]!, Tolerance);
}
// ====================== 5. List<T> + double ======================
[Fact]
public void Plus_ListT_Double_Returns_ListT()
{
var result = unitsList.Plus<Length>(scalarDouble);
Assert.Equal(2, result.Count);
Assert.Equal(1000 + 500, (double)result[0], Tolerance);
Assert.Equal(1 + 500, (double)result[1], Tolerance);
}
// ====================== 6. List<T?> + double ======================
[Fact]
public void Plus_ListNullableT_Double_Returns_ListNullableT()
{
var result = nullableUnitsList.Plus<Length>(scalarDouble);
Assert.Equal(3, result.Count);
Assert.Equal(1000 + 500, (double)result[0]!, Tolerance);
Assert.Null(result[1]); // null + число = null
Assert.Equal(1 + 500, (double)result[2]!, Tolerance);
}
// ====================== 7. double + List<T> ======================
[Fact]
public void Plus_Double_ListT_Returns_ListT()
{
var result = scalarDouble.Plus<Length>(unitsList);
Assert.Equal(2, result.Count);
Assert.Equal(500 + 1000, (double)result[0], Tolerance);
Assert.Equal(500 + 1, (double)result[1], Tolerance);
}
// ====================== 8. double + List<T?> ======================
[Fact]
public void Plus_Double_ListNullableT_Returns_ListNullableT()
{
var result = scalarDouble.Plus<Length>(nullableUnitsList);
Assert.Equal(3, result.Count);
Assert.Equal(500 + 1000, (double)result[0]!, Tolerance);
Assert.Null(result[1]); // число + null = null
Assert.Equal(500 + 1, (double)result[2]!, Tolerance);
}
// ====================== 9. IReadOnlyCollection<T> + double (Span) ======================
[Fact]
public void Plus_IReadOnlyCollectionT_Double_Span()
{
Span<Length> dest = new Length[2];
((IReadOnlyCollection<Length>)unitsArray).Plus<Length>(scalarDouble, dest);
Assert.Equal(1000 + 500, (double)dest[0], Tolerance);
Assert.Equal(1 + 500, (double)dest[1], Tolerance);
}
// ====================== 10. IReadOnlyCollection<T?> + double (Span) ======================
[Fact]
public void Plus_IReadOnlyCollectionNullableT_Double_Span()
{
Span<Length?> dest = new Length?[3];
((IReadOnlyCollection<Length?>)nullableUnitsArray).Plus<Length>(scalarDouble, dest);
Assert.Equal(1000 + 500, (double)dest[0]!, Tolerance);
Assert.Null(dest[1]); // null + число = null
Assert.Equal(1 + 500, (double)dest[2]!, Tolerance);
}
// ====================== 11. double + IReadOnlyCollection<T> (Span) ======================
[Fact]
public void Plus_Double_IReadOnlyCollectionT_Span()
{
Span<Length> dest = new Length[2];
scalarDouble.Plus<Length>((IReadOnlyCollection<Length>)unitsArray, dest);
Assert.Equal(500 + 1000, (double)dest[0], Tolerance);
Assert.Equal(500 + 1, (double)dest[1], Tolerance);
}
// ====================== 12. double + IReadOnlyCollection<T?> (Span) ======================
[Fact]
public void Plus_Double_IReadOnlyCollectionNullableT_Span()
{
Span<Length?> dest = new Length?[3];
scalarDouble.Plus<Length>((IReadOnlyCollection<Length?>)nullableUnitsArray, dest);
Assert.Equal(500 + 1000, (double)dest[0]!, Tolerance);
Assert.Null(dest[1]); // число + null = null
Assert.Equal(500 + 1, (double)dest[2]!, Tolerance);
}
// ====================== 13. IEnumerable<T> + double ======================
[Fact]
public void Plus_IEnumerableT_Double_Returns_IEnumerableT()
{
var result = ((IEnumerable<Length>)unitsArray).Plus<Length>(scalarDouble).ToList();
Assert.Equal(2, result.Count);
Assert.Equal(1000 + 500, (double)result[0], Tolerance);
Assert.Equal(1 + 500, (double)result[1], Tolerance);
}
// ====================== 14. IEnumerable<T?> + double ======================
[Fact]
public void Plus_IEnumerableNullableT_Double_Returns_IEnumerableNullableT()
{
var result = ((IEnumerable<Length?>)nullableUnitsArray).Plus<Length>(scalarDouble).ToList();
Assert.Equal(3, result.Count);
Assert.Equal(1000 + 500, (double)result[0]!, Tolerance);
Assert.Null(result[1]); // null + число = null
Assert.Equal(1 + 500, (double)result[2]!, Tolerance);
}
// ====================== 15. double + IEnumerable<T> ======================
[Fact]
public void Plus_Double_IEnumerableT_Returns_IEnumerableT()
{
var result = scalarDouble.Plus<Length>((IEnumerable<Length>)unitsArray).ToList();
Assert.Equal(2, result.Count);
Assert.Equal(500 + 1000, (double)result[0], Tolerance);
Assert.Equal(500 + 1, (double)result[1], Tolerance);
}
// ====================== 16. double + IEnumerable<T?> ======================
[Fact]
public void Plus_Double_IEnumerableNullableT_Returns_IEnumerableNullableT()
{
var result = scalarDouble.Plus<Length>((IEnumerable<Length?>)nullableUnitsArray).ToList();
Assert.Equal(3, result.Count);
Assert.Equal(500 + 1000, (double)result[0]!, Tolerance);
Assert.Null(result[1]); // число + null = null
Assert.Equal(500 + 1, (double)result[2]!, Tolerance);
}
// ====================== 17. double[] + T ======================
[Fact]
public void Plus_DoubleArray_T_Returns_TArray()
{
var result = doubleArray.Plus(scalarUnit);
Assert.Equal(2, result.Length);
Assert.Equal(200 + 1000, (double)result[0], Tolerance);
Assert.Equal(300 + 1000, (double)result[1], Tolerance);
}
// ====================== 18. double?[] + T ======================
[Fact]
public void Plus_NullableDoubleArray_T_Returns_NullableTArray()
{
var result = nullableDoubleArray.Plus(scalarUnit);
Assert.Equal(3, result.Length);
Assert.Equal(200 + 1000, (double)result[0]!, Tolerance);
Assert.Null(result[1]); // null + Length = null
Assert.Equal(300 + 1000, (double)result[2]!, Tolerance);
}
// ====================== 19. T + double[] ======================
[Fact]
public void Plus_T_DoubleArray_Returns_TArray()
{
var result = scalarUnit.Plus(doubleArray);
Assert.Equal(2, result.Length);
Assert.Equal(1000 + 200, (double)result[0], Tolerance);
Assert.Equal(1000 + 300, (double)result[1], Tolerance);
}
// ====================== 20. T + double?[] ======================
[Fact]
public void Plus_T_NullableDoubleArray_Returns_NullableTArray()
{
var result = scalarUnit.Plus(nullableDoubleArray);
Assert.Equal(3, result.Length);
Assert.Equal(1000 + 200, (double)result[0]!, Tolerance);
Assert.Null(result[1]); // Length + null = null
Assert.Equal(1000 + 300, (double)result[2]!, Tolerance);
}
// ====================== 21. List<double> + T ======================
[Fact]
public void Plus_ListDouble_T_Returns_ListT()
{
var result = doubleList.Plus(scalarUnit);
Assert.Equal(2, result.Count);
Assert.Equal(200 + 1000, (double)result[0], Tolerance);
Assert.Equal(300 + 1000, (double)result[1], Tolerance);
}
// ====================== 22. List<double?> + T ======================
[Fact]
public void Plus_ListNullableDouble_T_Returns_ListNullableT()
{
var result = nullableDoubleList.Plus(scalarUnit);
Assert.Equal(3, result.Count);
Assert.Equal(200 + 1000, (double)result[0]!, Tolerance);
Assert.Null(result[1]); // null + Length = null
Assert.Equal(300 + 1000, (double)result[2]!, Tolerance);
}
// ====================== 23. T + List<double> ======================
[Fact]
public void Plus_T_ListDouble_Returns_ListT()
{
var result = scalarUnit.Plus(doubleList);
Assert.Equal(2, result.Count);
Assert.Equal(1000 + 200, (double)result[0], Tolerance);
Assert.Equal(1000 + 300, (double)result[1], Tolerance);
}
// ====================== 24. T + List<double?> ======================
[Fact]
public void Plus_T_ListNullableDouble_Returns_ListNullableT()
{
var result = scalarUnit.Plus(nullableDoubleList);
Assert.Equal(3, result.Count);
Assert.Equal(1000 + 200, (double)result[0]!, Tolerance);
Assert.Null(result[1]); // Length + null = null
Assert.Equal(1000 + 300, (double)result[2]!, Tolerance);
}
// ====================== 25. T + T[] -> double[] ======================
[Fact]
public void Plus_T_TArray_Returns_DoubleArray()
{
var result = scalarUnit + unitsArray;
Assert.Equal(2, result.Length);
Assert.Equal(1000 + 1000, result[0]._Value, Tolerance);
Assert.Equal(1000 + 1, result[1]._Value, Tolerance);
}
// Дополнительно: перегрузки с результатом double (без R)
[Fact]
public void Plus_TArray_T_Returns_DoubleArray()
{
var result = unitsArray + scalarUnit;
Assert.Equal(2, result.Length);
Assert.Equal(1000 + 1000, result[0]._Value, Tolerance);
Assert.Equal(1 + 1000, result[1]._Value, Tolerance);
}
[Fact]
public void Plus_ListT_T_Returns_DoubleList()
{
var result = unitsList + scalarUnit;
Assert.Equal(2, result.Count);
Assert.Equal(1000 + 1000, result[0]._Value, Tolerance);
Assert.Equal(1 + 1000, result[1]._Value, Tolerance);
}
// ====================== Обработка null коллекций ======================
[Fact]
public void Plus_NullArray_ReturnsNull()
{
Length[] nullArray = null;
var result = nullArray.Plus<Length>(5.0);
Assert.Null(result);
}
[Fact]
public void Plus_NullList_ReturnsNull()
{
List<Length> nullList = null;
var result = nullList.Plus<Length>(5.0);
Assert.Null(result);
}
[Fact]
public void Plus_EmptyArray_ReturnsEmptyArray()
{
var empty = Array.Empty<Length>();
var result = empty.Plus<Length>(5.0);
Assert.Empty(result);
}
}
}

View File

@@ -0,0 +1,73 @@
namespace QWERTYkez.Mensura.Tests;
public class CollectionsPow2Extensions
{
[Fact]
public void Pow2_Array_CalculatesCorrectly()
{
// Arrange
Length[] source = [new(2), new(3), new(10)];
// Act
Length[] result = source.Pow2<Length, Length>();
// Assert
Assert.Equal(4, result[0]._Value);
Assert.Equal(9, result[1]._Value);
Assert.Equal(100, result[2]._Value);
}
[Fact]
public void Pow2_NullableArray_HandlesNulls()
{
// Arrange
Length?[] source = [new(5), null, new(4)];
// Act
Length?[] result = source.Pow2<Length, Length>();
// Assert
Assert.Equal(25, result[0]?._Value);
Assert.Null(result[1]);
Assert.Equal(16, result[2]?._Value);
}
[Fact]
public void Pow2_List_ReturnsCorrectResult()
{
// Arrange
var source = new List<Length> { new(2), new(8) };
// Act
var result = source.Pow2<Length, Length>();
// Assert
Assert.Equal(4, result[0]._Value);
Assert.Equal(64, result[1]._Value);
}
[Fact]
public void Pow2_IEnumerable_ProcessesCorrectly()
{
// Arrange
IEnumerable<Length> source = new HashSet<Length> { new(3), new(7) };
// Act
var result = source.Pow2<Length, Length>().ToList();
// Assert
Assert.Contains(result, x => x._Value == 9);
Assert.Contains(result, x => x._Value == 49);
}
[Fact]
public void Pow2_DestinationTooShort_ThrowsArgumentException()
{
// Arrange
Length[] source = [new(2), new(3)];
Length[] dest = new Length[1];
// Act & Assert
Assert.Throws<ArgumentException>(() => source.Pow2(dest));
}
}

View File

@@ -0,0 +1,59 @@
namespace QWERTYkez.Mensura.Tests;
public class Pow3ExtensionsTests
{
[Fact]
public void Pow3_Array_CalculatesCorrectly()
{
// Arrange: 2^3=8, 3^3=27, 4^3=64
Length[] source = [new(2), new(3), new(4)];
// Act
Length[] result = source.Pow3<Length, Length>();
// Assert
Assert.Equal(8, result[0]._Value);
Assert.Equal(27, result[1]._Value);
Assert.Equal(64, result[2]._Value);
}
[Fact]
public void Pow3_NullableArray_HandlesNulls()
{
// Arrange
Length?[] source = [new(5), null, new(10)];
// Act
Length?[] result = source.Pow3<Length, Length>();
// Assert
Assert.Equal(125, result[0]?._Value);
Assert.Null(result[1]);
Assert.Equal(1000, result[2]?._Value);
}
[Fact]
public void Pow3_List_ReturnsCorrectResult()
{
// Arrange
var source = new List<Length> { new(2), new(5) };
// Act
var result = source.Pow3<Length, Length>();
// Assert
Assert.Equal(8, result[0]._Value);
Assert.Equal(125, result[1]._Value);
}
[Fact]
public void Pow3_DestinationTooShort_ThrowsArgumentException()
{
// Arrange
Length[] source = [new(2), new(3)];
Length[] dest = new Length[1];
// Act & Assert
Assert.Throws<ArgumentException>(() => source.Pow3(dest));
}
}

View File

@@ -0,0 +1,75 @@
namespace QWERTYkez.Mensura.Tests;
public class CollectionsPowNExtensions
{
private const double BaseVal = 3.0;
private const double Expected = 9.0; // 3^2 = 9
[Fact]
public void Test_PowN()
{
Length[] arr = [new(BaseVal)];
Length?[] arrNull = [new(BaseVal), null];
List<Length> list = [new(BaseVal)];
List<Length?> listNull = [new(BaseVal), null];
IReadOnlyCollection<Length> roc = list;
IEnumerable<Length> en = list;
IEnumerable<Length?> enNull = listNull;
// --- 1. Целые степени (int) ---
// Span-based Core
Span<Length> dstSpan = new Length[1];
arr.AsSpan().PowCore(2, 1, dstSpan);
Assert.Equal(Expected, dstSpan[0]._Value);
arrNull.AsSpan().PowCore(2, 2, new Length?[2]); // Проверка nullable span
arr.AsSpan().Pow(2, dstSpan);
Assert.Equal(Expected, dstSpan[0]._Value);
// Arrays
Assert.Equal(Expected, arr.Pow<Length, Length>(2)[0]._Value);
Assert.Equal(Expected, arrNull.Pow<Length, Length>(2)[0]?._Value);
// Lists
Assert.Equal(Expected, list.Pow<Length, Length>(2)[0]._Value);
Assert.Equal(Expected, listNull.Pow<Length, Length>(2)[0]?._Value);
// ReadOnlyCollection
roc.Pow(2, dstSpan);
Assert.Equal(Expected, dstSpan[0]._Value);
// IEnumerable / Iterators
Assert.Equal(Expected, en.Pow<Length, Length>(2).First()._Value);
Assert.Equal(Expected, enNull.Pow<Length, Length>(2).First(x => x.HasValue)?._Value);
// --- 2. Дробные степени (double) ---
// Span-based Core
arr.AsSpan().PowCore(2.0, 1, dstSpan);
Assert.Equal(Expected, dstSpan[0]._Value);
arr.AsSpan().Pow(2.0, dstSpan);
Assert.Equal(Expected, dstSpan[0]._Value);
// Arrays
Assert.Equal(Expected, arr.Pow<Length, Length>(2.0)[0]._Value);
Assert.Equal(Expected, arrNull.Pow<Length, Length>(2.0)[0]?._Value);
// Lists
Assert.Equal(Expected, list.Pow<Length, Length>(2.0)[0]._Value);
Assert.Equal(Expected, listNull.Pow<Length, Length>(2.0)[0]?._Value);
// ReadOnlyCollection
roc.Pow(2.0, dstSpan);
Assert.Equal(Expected, dstSpan[0]._Value);
// IEnumerable / Iterators
Assert.Equal(Expected, en.Pow<Length, Length>(2.0).First()._Value);
Assert.Equal(Expected, enNull.Pow<Length, Length>(2.0).First(x => x.HasValue)?._Value);
// Дополнительные итераторы
Assert.Equal(Expected, en.PowIterator<Length, Length>(2.0).First()._Value);
Assert.Equal(Expected, enNull.PowNullableIterator<Length, Length>(2.0).First(x => x.HasValue)?._Value);
}
}

View File

@@ -0,0 +1,59 @@
namespace QWERTYkez.Mensura.Tests;
public class CollectionsRootOfCubeExtensions
{
[Fact]
public void Cbrt_Array_CalculatesCorrectly()
{
Length[] source = [new(4), new(9), new(16), new(25)];
// Ожидаем корень, но важно: Math.Sqrt(4) = 2.
// Если ваш код делает специфичные преобразования, адаптируйте Assert.
var result = source.Cbrt<Length, Length>();
Assert.Equal(2, result[0]._Value);
Assert.Equal(3, result[1]._Value);
Assert.Equal(4, result[2]._Value);
Assert.Equal(5, result[3]._Value);
}
[Fact]
public void Cbrt_NullableArray_HandlesNulls()
{
Length?[] source = [new(16), null, new(36)];
var result = source.Cbrt<Length, Length>();
Assert.Equal(4, result[0]?._Value);
Assert.Null(result[1]);
Assert.Equal(6, result[2]?._Value);
}
[Fact]
public void Cbrt_DestinationTooShort_ThrowsArgumentException()
{
Length[] source = [new(1), new(4)];
Length[] dest = new Length[1];
Assert.Throws<ArgumentException>(() => source.Cbrt(dest));
}
[Fact]
public void Cbrt_List_WorksCorrectly()
{
var list = new List<Length> { new(1), new(4) };
var result = list.Cbrt<Length, Length>();
Assert.Equal(2, result.Count);
Assert.Equal(1, result[0]._Value);
Assert.Equal(2, result[1]._Value);
}
[Fact]
public void Cbrt_IEnumerable_NoExtraAllocations()
{
IEnumerable<Length> source = new HashSet<Length> { new(9), new(81) };
var result = source.Cbrt<Length, Length>().ToList();
Assert.Contains(result, x => x._Value == 3);
Assert.Contains(result, x => x._Value == 9);
}
}

View File

@@ -0,0 +1,75 @@
namespace QWERTYkez.Mensura.Tests;
public class CollectionsRootOfSquareExtensions
{
[Fact]
public void Sqrt_Array_ShouldCalculateCorrectly()
{
// Arrange
Length[] source = [new(4), new(9), new(16)];
// Act
Length[] result = source.Sqrt<Length, Length>();
// Assert
Assert.Equal(2, result[0]._Value);
Assert.Equal(3, result[1]._Value);
Assert.Equal(4, result[2]._Value);
}
[Fact]
public void Sqrt_NullableArray_ShouldHandleNulls()
{
// Arrange
Length?[] source = [new(25), null, new(100)];
// Act
Length?[] result = source.Sqrt<Length, Length>();
// Assert
Assert.Equal(5, result[0]?._Value);
Assert.Null(result[1]);
Assert.Equal(10, result[2]?._Value);
}
[Fact]
public void Sqrt_List_ShouldReturnCorrectList()
{
// Arrange
var source = new List<Length> { new(1), new(4), new(9) };
// Act
var result = source.Sqrt<Length, Length>();
// Assert
Assert.Equal(3, result.Count);
Assert.Equal(1, result[0]._Value);
Assert.Equal(2, result[1]._Value);
Assert.Equal(3, result[2]._Value);
}
[Fact]
public void Sqrt_DestinationTooShort_ShouldThrowArgumentException()
{
// Arrange
Length[] source = [new(4), new(9)];
Length[] dest = new Length[1]; // Слишком мало
// Act & Assert
Assert.Throws<ArgumentException>(() => source.AsSpan().Sqrt(dest.AsSpan()));
}
[Fact]
public void Sqrt_IEnumerable_ShouldWorkWithGenericCollection()
{
// Arrange
IEnumerable<Length> source = new HashSet<Length> { new(16), new(64) };
// Act
var result = source.Sqrt<Length, Length>().ToList();
// Assert
Assert.Contains(result, x => x._Value == 4);
Assert.Contains(result, x => x._Value == 8);
}
}

View File

@@ -0,0 +1,164 @@
namespace QWERTYkez.Mensura.Tests;
public class DoubleExtensions
{
#region 1. Тесты скалярных типов (Обычные, Экзотические и Nullable)
[Fact]
public void ToDouble_Should_Convert_ExoticTypes_Without_InvalidCastException()
{
// Проверяем типы, которые раньше могли падать в рантайме из-за Convert.ToDouble(object)
Half halfValue = (Half)3.14f;
Int128 int128Value = Int128.Parse("1234567890123456789012345");
UInt128 uInt128Value = UInt128.Parse("9876543210987654321098765");
nint nintValue = 42;
// Act & Assert
Assert.Equal(3.14, halfValue.ToDouble(), 2);
Assert.Equal((double)int128Value, int128Value.ToDouble());
Assert.Equal((double)uInt128Value, uInt128Value.ToDouble());
Assert.Equal(42.0, nintValue.ToDouble());
}
[Theory]
[InlineData((int)100, 100.0)]
[InlineData((byte)5, 5.0)]
[InlineData((float)1.5f, 1.5)]
public void ToDouble_StandardScalars_ShouldConvertCorrectly(object input, double expected)
{
double result = input switch
{
int i => i.ToDouble(),
byte b => b.ToDouble(),
float f => f.ToDouble(),
_ => throw new ArgumentException("Unsupported type in test")
};
Assert.Equal(expected, result);
}
[Fact]
public void ToDouble_NullableScalars_Should_Return_Zero_When_Null()
{
// Arrange
int? nullInt = null;
Half? nullHalf = null;
Int128? nullInt128 = null;
int? validInt = 10;
Half? validHalf = (Half)2.5f;
// Act & Assert
Assert.Equal(0d, nullInt.ToDouble());
Assert.Equal(0d, nullHalf.ToDouble());
Assert.Equal(0d, nullInt128.ToDouble());
Assert.Equal(10d, validInt.ToDouble());
Assert.Equal(2.5d, validHalf.ToDouble(), 1);
}
#endregion
#region 2. Тесты SIMD и оптимизаций коллекций (Различные длины массивов)
[Theory]
[InlineData(0)] // Пустой массив
[InlineData(1)] // 1 элемент (чисто скалярный fallback)
[InlineData(7)] // Нечетное число элементов
[InlineData(16)] // Кратный размер (размер Vector128/Vector256)
[InlineData(35)] // Большой массив с остатком для скалярного хвоста
public void ToDouble_ArrayAndList_Should_Correctly_Map_Via_SIMD_Or_Fallback(int count)
{
// Arrange
float[] sourceArray = [.. Enumerable.Range(1, count).Select(x => x * 1.5f)];
List<float> sourceList = [.. sourceArray];
double[] destFromArray = new double[count];
double[] destFromList = new double[count];
// Act
// Явное приведение к IReadOnlyCollection устраняет ошибку CS0121 (неоднозначность вызова)
((IReadOnlyCollection<float>)sourceArray).ToDouble(destFromArray);
((IReadOnlyCollection<float>)sourceList).ToDouble(destFromList);
// Assert
for (int i = 0; i < count; i++)
{
double expected = sourceArray[i];
Assert.Equal(expected, destFromArray[i]);
Assert.Equal(expected, destFromList[i]);
}
}
#endregion
#region 3. Тесты Nullable-коллекций (Проверка на null-элементы внутри)
[Fact]
public void ToDouble_Nullable_Collections_Should_Keep_Nulls_In_Destination()
{
// Arrange
UInt128?[] sourceArray = [10, null, 20, null, 30];
double?[] destination = new double?[sourceArray.Length];
// Act
((IReadOnlyCollection<UInt128?>)sourceArray).ToDouble(destination);
// Assert
Assert.Equal(10d, destination[0]);
Assert.Null(destination[1]);
Assert.Equal(20d, destination[2]);
Assert.Null(destination[3]);
Assert.Equal(30d, destination[4]);
}
#endregion
#region 4. Тесты LINQ / Отложенного выполнения (IEnumerable)
[Fact]
public void ToDouble_IEnumerable_Extension_Should_Handle_Execution_Types_Correctly()
{
// Arrange
int[] array = [1, 2, 3];
List<int> list = [4, 5, 6];
IEnumerable<int> genericEnum = Enumerable.Range(7, 3);
// Act
var resFromArray = array.ToDouble(); // Быстрый бранч для массива
var resFromList = list.ToDouble(); // Быстрый бранч для списка
var resFromEnum = genericEnum.ToDouble(); // Медленный итератор по IEnumerable
// Assert
Assert.Equal([1d, 2d, 3d], resFromArray);
Assert.Equal([4d, 5d, 6d], resFromList);
Assert.Equal([7d, 8d, 9d], resFromEnum);
}
#endregion
#region 5. Тесты безопасности и валидации аргументов
[Fact]
public void ToDouble_Should_Throw_ArgumentException_When_Destination_Is_Too_Short()
{
// Arrange
int[] source = [1, 2, 3, 4, 5];
double[] destinationTooShort = new double[4]; // Нужен размер >= 5
// Act & Assert
Assert.Throws<ArgumentException>(() => ((IReadOnlyCollection<int>)source).ToDouble(destinationTooShort));
}
[Fact]
public void ToDouble_Should_Do_Nothing_And_Return_When_Collection_Is_Empty_Or_Null()
{
// Arrange
int[]? nullArray = null;
int[] emptyArray = [];
double[] destination = new double[5];
// Act & Assert (Не должно выбрасывать NullReferenceException или ArgumentException)
((IReadOnlyCollection<int>?)nullArray)!.ToDouble(destination);
((IReadOnlyCollection<int>)emptyArray).ToDouble(destination);
Assert.All(destination, x => Assert.Equal(0d, x)); // Назначение осталось нетронутым (все нули по умолчанию)
}
#endregion
}

View File

@@ -7,6 +7,14 @@
<IsPackable>false</IsPackable> <IsPackable>false</IsPackable>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Debug|net10.0|AnyCPU'">
<NoWarn>1701;1702;IDE0221</NoWarn>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Release|net10.0|AnyCPU'">
<NoWarn>1701;1702;IDE0221</NoWarn>
</PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="coverlet.collector" Version="10.0.1"> <PackageReference Include="coverlet.collector" Version="10.0.1">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
@@ -23,6 +31,8 @@
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\QWERTYkez.Mensura\QWERTYkez.Mensura.csproj" /> <ProjectReference Include="..\QWERTYkez.Mensura\QWERTYkez.Mensura.csproj" />
<InternalsVisibleTo Include="..\QWERTYkez.Mensura\QWERTYkez.Mensura.csproj" />
<ProjectReference Include="..\QWERTYkez.Mensura.Generator\QWERTYkez.Mensura.Generator.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" /> <ProjectReference Include="..\QWERTYkez.Mensura.Generator\QWERTYkez.Mensura.Generator.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
</ItemGroup> </ItemGroup>

View File

@@ -0,0 +1,2 @@
global using QWERTYkez.Mensura.Extensions;
global using QWERTYkez.Mensura.Units;

View File

@@ -296,80 +296,6 @@ internal static partial class AggregateUnitExtensions
return CollectionsMarshal.AsSpan(list).Min(); return CollectionsMarshal.AsSpan(list).Min();
} }
// === ICollection<T> ===
internal static T Sum<T>(this ICollection<T> collection)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (collection == null || collection.Count == 0) return default;
if (collection is T[] array) return array.Sum();
if (collection is List<T> list) return list.Sum();
T[] sharedArray = ArrayPool<T>.Shared.Rent(collection.Count);
try
{
collection.CopyTo(sharedArray, 0); // Встроенный CopyTo работает быстрее, чем ручной foreach
return new ReadOnlySpan<T>(sharedArray, 0, collection.Count).Sum();
}
finally
{
ArrayPool<T>.Shared.Return(sharedArray);
}
}
internal static T Avg<T>(this ICollection<T> collection)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (collection == null || collection.Count == 0) return double.NaN.ToUnit<T>();
if (collection is T[] array) return array.Avg();
if (collection is List<T> list) return list.Avg();
T[] sharedArray = ArrayPool<T>.Shared.Rent(collection.Count);
try
{
collection.CopyTo(sharedArray, 0);
return new ReadOnlySpan<T>(sharedArray, 0, collection.Count).Avg();
}
finally
{
ArrayPool<T>.Shared.Return(sharedArray);
}
}
internal static T Max<T>(this ICollection<T> collection)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (collection == null || collection.Count == 0) return double.MinValue.ToUnit<T>();
if (collection is T[] array) return array.Max();
if (collection is List<T> list) return list.Max();
T[] sharedArray = ArrayPool<T>.Shared.Rent(collection.Count);
try
{
collection.CopyTo(sharedArray, 0);
return new ReadOnlySpan<T>(sharedArray, 0, collection.Count).Max();
}
finally
{
ArrayPool<T>.Shared.Return(sharedArray);
}
}
internal static T Min<T>(this ICollection<T> collection)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (collection == null || collection.Count == 0) return double.MaxValue.ToUnit<T>();
if (collection is T[] array) return array.Min();
if (collection is List<T> list) return list.Min();
T[] sharedArray = ArrayPool<T>.Shared.Rent(collection.Count);
try
{
collection.CopyTo(sharedArray, 0);
return new ReadOnlySpan<T>(sharedArray, 0, collection.Count).Min();
}
finally
{
ArrayPool<T>.Shared.Return(sharedArray);
}
}
// === IReadOnlyCollection<T> === // === IReadOnlyCollection<T> ===
internal static T Sum<T>(this IReadOnlyCollection<T> collection) internal static T Sum<T>(this IReadOnlyCollection<T> collection)
where T : struct, IMensuraUnit, IEquatable<T> where T : struct, IMensuraUnit, IEquatable<T>
@@ -446,7 +372,6 @@ internal static partial class AggregateUnitExtensions
// Быстрый SIMD-путь для готовых коллекций (0 аллокаций) // Быстрый SIMD-путь для готовых коллекций (0 аллокаций)
if (collection is T[] array) return array.Sum(); if (collection is T[] array) return array.Sum();
if (collection is List<T> list) return list.Sum(); if (collection is List<T> list) return list.Sum();
if (collection is ICollection<T> col) return col.Sum();
if (collection is IReadOnlyCollection<T> roc) return roc.Sum(); if (collection is IReadOnlyCollection<T> roc) return roc.Sum();
// Медленный путь для yield return: считаем на лету без буферов // Медленный путь для yield return: считаем на лету без буферов
@@ -468,7 +393,6 @@ internal static partial class AggregateUnitExtensions
if (collection is T[] array) return array.Avg(); if (collection is T[] array) return array.Avg();
if (collection is List<T> list) return list.Avg(); if (collection is List<T> list) return list.Avg();
if (collection is ICollection<T> col) return col.Avg();
if (collection is IReadOnlyCollection<T> roc) return roc.Avg(); if (collection is IReadOnlyCollection<T> roc) return roc.Avg();
double sum = 0; double sum = 0;
@@ -492,7 +416,6 @@ internal static partial class AggregateUnitExtensions
if (collection is T[] array) return array.Max(); if (collection is T[] array) return array.Max();
if (collection is List<T> list) return list.Max(); if (collection is List<T> list) return list.Max();
if (collection is ICollection<T> col) return col.Max();
if (collection is IReadOnlyCollection<T> roc) return roc.Max(); if (collection is IReadOnlyCollection<T> roc) return roc.Max();
double max = double.MinValue; double max = double.MinValue;
@@ -514,7 +437,6 @@ internal static partial class AggregateUnitExtensions
if (collection is T[] array) return array.Min(); if (collection is T[] array) return array.Min();
if (collection is List<T> list) return list.Min(); if (collection is List<T> list) return list.Min();
if (collection is ICollection<T> col) return col.Min();
if (collection is IReadOnlyCollection<T> roc) return roc.Min(); if (collection is IReadOnlyCollection<T> roc) return roc.Min();
double min = double.MaxValue; double min = double.MaxValue;
@@ -708,80 +630,6 @@ internal static partial class AggregateUnitExtensions
return CollectionsMarshal.AsSpan(list).Min(); return CollectionsMarshal.AsSpan(list).Min();
} }
// === ICollection<T> ===
internal static T Sum<T>(this ICollection<T?> collection)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (collection == null || collection.Count == 0) return default;
if (collection is T?[] array) return array.Sum();
if (collection is List<T?> list) return list.Sum();
T?[] sharedArray = ArrayPool<T?>.Shared.Rent(collection.Count);
try
{
collection.CopyTo(sharedArray, 0);
return new ReadOnlySpan<T?>(sharedArray, 0, collection.Count).Sum();
}
finally
{
ArrayPool<T?>.Shared.Return(sharedArray);
}
}
internal static T Avg<T>(this ICollection<T?> collection)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (collection == null || collection.Count == 0) return double.NaN.ToUnit<T>();
if (collection is T?[] array) return array.Avg();
if (collection is List<T?> list) return list.Avg();
T?[] sharedArray = ArrayPool<T?>.Shared.Rent(collection.Count);
try
{
collection.CopyTo(sharedArray, 0);
return new ReadOnlySpan<T?>(sharedArray, 0, collection.Count).Avg();
}
finally
{
ArrayPool<T?>.Shared.Return(sharedArray);
}
}
internal static T Max<T>(this ICollection<T?> collection)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (collection == null || collection.Count == 0) return double.MinValue.ToUnit<T>();
if (collection is T?[] array) return array.Max();
if (collection is List<T?> list) return list.Max();
T?[] sharedArray = ArrayPool<T?>.Shared.Rent(collection.Count);
try
{
collection.CopyTo(sharedArray, 0);
return new ReadOnlySpan<T?>(sharedArray, 0, collection.Count).Max();
}
finally
{
ArrayPool<T?>.Shared.Return(sharedArray);
}
}
internal static T Min<T>(this ICollection<T?> collection)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (collection == null || collection.Count == 0) return double.MaxValue.ToUnit<T>();
if (collection is T?[] array) return array.Min();
if (collection is List<T?> list) return list.Min();
T?[] sharedArray = ArrayPool<T?>.Shared.Rent(collection.Count);
try
{
collection.CopyTo(sharedArray, 0);
return new ReadOnlySpan<T?>(sharedArray, 0, collection.Count).Min();
}
finally
{
ArrayPool<T?>.Shared.Return(sharedArray);
}
}
// === IReadOnlyCollection<T> === // === IReadOnlyCollection<T> ===
internal static T Sum<T>(this IReadOnlyCollection<T?> collection) internal static T Sum<T>(this IReadOnlyCollection<T?> collection)
where T : struct, IMensuraUnit, IEquatable<T> where T : struct, IMensuraUnit, IEquatable<T>
@@ -857,7 +705,6 @@ internal static partial class AggregateUnitExtensions
// Быстрый SIMD-путь для готовых коллекций за 0 аллокаций // Быстрый SIMD-путь для готовых коллекций за 0 аллокаций
if (collection is T?[] array) return array.Sum(); if (collection is T?[] array) return array.Sum();
if (collection is List<T?> list) return list.Sum(); if (collection is List<T?> list) return list.Sum();
if (collection is ICollection<T?> col) return col.Sum();
if (collection is IReadOnlyCollection<T?> roc) return roc.Sum(); if (collection is IReadOnlyCollection<T?> roc) return roc.Sum();
// Медленный путь для ленивого yield return: вычисляем на лету // Медленный путь для ленивого yield return: вычисляем на лету
@@ -882,7 +729,6 @@ internal static partial class AggregateUnitExtensions
if (collection is T?[] array) return array.Avg(); if (collection is T?[] array) return array.Avg();
if (collection is List<T?> list) return list.Avg(); if (collection is List<T?> list) return list.Avg();
if (collection is ICollection<T?> col) return col.Avg();
if (collection is IReadOnlyCollection<T?> roc) return roc.Avg(); if (collection is IReadOnlyCollection<T?> roc) return roc.Avg();
double sum = 0; double sum = 0;
@@ -909,7 +755,6 @@ internal static partial class AggregateUnitExtensions
if (collection is T?[] array) return array.Max(); if (collection is T?[] array) return array.Max();
if (collection is List<T?> list) return list.Max(); if (collection is List<T?> list) return list.Max();
if (collection is ICollection<T?> col) return col.Max();
if (collection is IReadOnlyCollection<T?> roc) return roc.Max(); if (collection is IReadOnlyCollection<T?> roc) return roc.Max();
double max = double.MinValue; double max = double.MinValue;
@@ -934,7 +779,6 @@ internal static partial class AggregateUnitExtensions
if (collection is T?[] array) return array.Min(); if (collection is T?[] array) return array.Min();
if (collection is List<T?> list) return list.Min(); if (collection is List<T?> list) return list.Min();
if (collection is ICollection<T?> col) return col.Min();
if (collection is IReadOnlyCollection<T?> roc) return roc.Min(); if (collection is IReadOnlyCollection<T?> roc) return roc.Min();
double min = double.MaxValue; double min = double.MaxValue;

View File

@@ -1,51 +1,35 @@
namespace QWERTYkez.Mensura.Extensions; using System.Reflection;
using System.Reflection.Emit;
namespace QWERTYkez.Mensura.Extensions;
internal static partial class CastExtensions internal static partial class CastExtensions
{ {
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static double ToDouble<T>(this T unit) internal static double ToDouble<T>(this T unit) where T : struct, IMensuraUnit, IEquatable<T>
where T : struct, IMensuraUnit, IEquatable<T> => Unsafe.As<T, double>(ref unit);
{
return Unsafe.As<T, double>(ref unit);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static T ToUnit<T>(this double val)
where T : struct, IMensuraUnit, IEquatable<T>
{
return Unsafe.As<double, T>(ref val);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static void SetCountUnsafe<T>(this List<T> list, int count)
{
// Берем адрес управляемого объекта List в памяти
// Объект передается по ref-ссылке, преобразуется в указатель
ref var mimic = ref Unsafe.As<List<T>, Mimics<T>>(ref list);
// Меняем приватный размер напрямую в памяти объекта!
mimic.Size = count;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)] internal static T ToUnit<T>(this double val)
where T : struct, IMensuraUnit, IEquatable<T> => Unsafe.As<double, T>(ref val);
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static List<R> WrapAsList<T, R>(this T[] array) internal static List<R> WrapAsList<T, R>(this T[] array)
where T : struct, IMensuraUnit, IEquatable<T> where T : struct, IMensuraUnit, IEquatable<T>
where R : struct, IMensuraUnit, IEquatable<R> where R : struct, IMensuraUnit, IEquatable<R>
{ {
// Создаём пустой список с нулевой ёмкостью if (array is null) return null!;
// 1. Физически меняем паспорт (MethodTable) массива на целевой тип R[]
object arrObj = array;
unsafe { *(nint*)*(nint*)Unsafe.AsPointer(ref arrObj) = typeof(R[]).TypeHandle.Value; }
R[] morphedArray = Unsafe.As<R[]>(arrObj);
// 2. Вшиваем его в новый список
var list = new List<R>(0); var list = new List<R>(0);
ref var mimic = ref Unsafe.As<List<R>, ListMimic<R>>(ref list);
// Получаем внутреннюю структуру списка mimic.Items = morphedArray;
ref var mimic = ref Unsafe.As<List<R>, Mimics<T>>(ref list);
// Подменяем массив и устанавливаем размер
mimic.Items = array;
mimic.Size = array.Length; mimic.Size = array.Length;
mimic.Version = 1; // любое ненулевое значение для консистенции mimic.Version = 1;
return list; return list;
} }
@@ -55,16 +39,17 @@ internal static partial class CastExtensions
where T : struct, IMensuraUnit, IEquatable<T> where T : struct, IMensuraUnit, IEquatable<T>
where R : struct, IMensuraUnit, IEquatable<R> where R : struct, IMensuraUnit, IEquatable<R>
{ {
// Создаём пустой список с нулевой ёмкостью if (array is null) return null!;
object arrObj = array;
unsafe { *(nint*)*(nint*)Unsafe.AsPointer(ref arrObj) = typeof(R?[]).TypeHandle.Value; }
R?[] morphedArray = Unsafe.As<R?[]>(arrObj);
var list = new List<R?>(0); var list = new List<R?>(0);
ref var mimic = ref Unsafe.As<List<R?>, ListMimic<R?>>(ref list);
// Получаем внутреннюю структуру списка mimic.Items = morphedArray;
ref var mimic = ref Unsafe.As<List<R?>, Mimics<T?>>(ref list);
// Подменяем массив и устанавливаем размер
mimic.Items = array;
mimic.Size = array.Length; mimic.Size = array.Length;
mimic.Version = 1; // любое ненулевое значение для консистенции mimic.Version = 1;
return list; return list;
} }
@@ -73,16 +58,17 @@ internal static partial class CastExtensions
internal static List<double> WrapAsList<T>(this T[] array) internal static List<double> WrapAsList<T>(this T[] array)
where T : struct, IMensuraUnit, IEquatable<T> where T : struct, IMensuraUnit, IEquatable<T>
{ {
// Создаём пустой список с нулевой ёмкостью if (array is null) return null!;
object arrObj = array;
unsafe { *(nint*)*(nint*)Unsafe.AsPointer(ref arrObj) = typeof(double[]).TypeHandle.Value; }
double[] morphedArray = Unsafe.As<double[]>(arrObj);
var list = new List<double>(0); var list = new List<double>(0);
ref var mimic = ref Unsafe.As<List<double>, ListMimic<double>>(ref list);
// Получаем внутреннюю структуру списка mimic.Items = morphedArray;
ref var mimic = ref Unsafe.As<List<double>, Mimics<T>>(ref list);
// Подменяем массив и устанавливаем размер
mimic.Items = array;
mimic.Size = array.Length; mimic.Size = array.Length;
mimic.Version = 1; // любое ненулевое значение для консистенции mimic.Version = 1;
return list; return list;
} }
@@ -91,16 +77,17 @@ internal static partial class CastExtensions
internal static List<double?> WrapAsList<T>(this T?[] array) internal static List<double?> WrapAsList<T>(this T?[] array)
where T : struct, IMensuraUnit, IEquatable<T> where T : struct, IMensuraUnit, IEquatable<T>
{ {
// Создаём пустой список с нулевой ёмкостью if (array is null) return null!;
object arrObj = array;
unsafe { *(nint*)*(nint*)Unsafe.AsPointer(ref arrObj) = typeof(double?[]).TypeHandle.Value; }
double?[] morphedArray = Unsafe.As<double?[]>(arrObj);
var list = new List<double?>(0); var list = new List<double?>(0);
ref var mimic = ref Unsafe.As<List<double?>, ListMimic<double?>>(ref list);
// Получаем внутреннюю структуру списка mimic.Items = morphedArray;
ref var mimic = ref Unsafe.As<List<double?>, Mimics<T?>>(ref list);
// Подменяем массив и устанавливаем размер
mimic.Items = array;
mimic.Size = array.Length; mimic.Size = array.Length;
mimic.Version = 1; // любое ненулевое значение для консистенции mimic.Version = 1;
return list; return list;
} }
@@ -108,16 +95,14 @@ internal static partial class CastExtensions
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static List<double> WrapAsList(this double[] array) internal static List<double> WrapAsList(this double[] array)
{ {
// Создаём пустой список с нулевой ёмкостью if (array is null) return null!;
// Массив уже имеет тип double[], подмена MethodTable не требуется
var list = new List<double>(0); var list = new List<double>(0);
ref var mimic = ref Unsafe.As<List<double>, ListMimic<double>>(ref list);
// Получаем внутреннюю структуру списка
ref var mimic = ref Unsafe.As<List<double>, Mimics<double>>(ref list);
// Подменяем массив и устанавливаем размер
mimic.Items = array; mimic.Items = array;
mimic.Size = array.Length; mimic.Size = array.Length;
mimic.Version = 1; // любое ненулевое значение для консистенции mimic.Version = 1;
return list; return list;
} }
@@ -125,28 +110,75 @@ internal static partial class CastExtensions
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static List<double?> WrapAsList(this double?[] array) internal static List<double?> WrapAsList(this double?[] array)
{ {
// Создаём пустой список с нулевой ёмкостью if (array is null) return null!;
// Массив уже имеет тип double?[], подмена MethodTable не требуется
var list = new List<double?>(0); var list = new List<double?>(0);
ref var mimic = ref Unsafe.As<List<double?>, ListMimic<double?>>(ref list);
// Получаем внутреннюю структуру списка
ref var mimic = ref Unsafe.As<List<double?>, Mimics<double?>>(ref list);
// Подменяем массив и устанавливаем размер
mimic.Items = array; mimic.Items = array;
mimic.Size = array.Length; mimic.Size = array.Length;
mimic.Version = 1; // любое ненулевое значение для консистенции mimic.Version = 1;
return list; return list;
} }
#region Инфраструктура Нативного Доступа (Zero-Overhead)
// Генератор сверхбыстрого доступа к приватным полям List<T> через чистый IL.
// Работает со скоростью обычного обращения к полю (компилируется в смещение указателя).
private static class ListAccessor<T>
{
public delegate ref T[] GetItemsRefDelegate(List<T> list);
public static readonly GetItemsRefDelegate GetItemsRef;
static ListAccessor()
{
var field = typeof(List<T>).GetField("_items", BindingFlags.NonPublic | BindingFlags.Instance)
?? throw new InvalidOperationException("CLR Layout changed: _items field not found.");
var dm = new DynamicMethod("GetItemsRef", typeof(T[]).MakeByRefType(), [typeof(List<T>)], typeof(ListAccessor<T>).Module, true);
var il = dm.GetILGenerator();
il.Emit(OpCodes.Ldarg_0); // Загружаем ссылку на List<T>
il.Emit(OpCodes.Ldflda, field); // Получаем управляемый указатель (ref) на поле _items
il.Emit(OpCodes.Ret); // Возвращаем его
GetItemsRef = dm.CreateDelegate<GetItemsRefDelegate>();
}
}
#endregion
#region Реализация ReCast со сменой MethodTable (In-Place Object Morphing)
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static List<R> ReCast<T, R>(this List<T> list) internal static List<R> ReCast<T, R>(this List<T> list)
where T : struct, IMensuraUnit, IEquatable<T> where T : struct, IMensuraUnit, IEquatable<T>
where R : struct, IMensuraUnit, IEquatable<R> where R : struct, IMensuraUnit, IEquatable<R>
{ {
return Unsafe.As<List<T>, List<R>>(ref list); if (list is null) return null!;
unsafe
{
// 1. Морфим внутренний массив со скоростью мысли
T[] items = ListAccessor<T>.GetItemsRef(list);
if (items is not null)
{
object itemsObj = items;
*(nint*)*(nint*)Unsafe.AsPointer(ref itemsObj) = typeof(R[]).TypeHandle.Value;
}
// 2. Морфим сам заголовок List
object listObj = list;
*(nint*)*(nint*)Unsafe.AsPointer(ref listObj) = typeof(List<R>).TypeHandle.Value;
return Unsafe.As<List<R>>(listObj);
}
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -154,21 +186,63 @@ internal static partial class CastExtensions
where T : struct, IMensuraUnit, IEquatable<T> where T : struct, IMensuraUnit, IEquatable<T>
where R : struct, IMensuraUnit, IEquatable<R> where R : struct, IMensuraUnit, IEquatable<R>
{ {
return Unsafe.As<List<T?>, List<R?>>(ref list); if (list is null) return null!;
unsafe
{
T?[] items = ListAccessor<T?>.GetItemsRef(list);
if (items is not null)
{
object itemsObj = items;
*(nint*)*(nint*)Unsafe.AsPointer(ref itemsObj) = typeof(R?[]).TypeHandle.Value;
}
object listObj = list;
*(nint*)*(nint*)Unsafe.AsPointer(ref listObj) = typeof(List<R?>).TypeHandle.Value;
return Unsafe.As<List<R?>>(listObj);
}
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static List<double> ReCast<T>(this List<T> list) internal static List<double> ReCast<T>(this List<T> list)
where T : struct, IMensuraUnit, IEquatable<T> where T : struct, IMensuraUnit, IEquatable<T>
{ {
return Unsafe.As<List<T>, List<double>>(ref list); if (list is null) return null!;
unsafe
{
T[] items = ListAccessor<T>.GetItemsRef(list);
if (items is not null)
{
object itemsObj = items;
*(nint*)*(nint*)Unsafe.AsPointer(ref itemsObj) = typeof(double[]).TypeHandle.Value;
}
object listObj = list;
*(nint*)*(nint*)Unsafe.AsPointer(ref listObj) = typeof(List<double>).TypeHandle.Value;
return Unsafe.As<List<double>>(listObj);
}
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static List<double?> ReCast<T>(this List<T?> list) internal static List<double?> ReCast<T>(this List<T?> list)
where T : struct, IMensuraUnit, IEquatable<T> where T : struct, IMensuraUnit, IEquatable<T>
{ {
return Unsafe.As<List<T?>, List<double?>>(ref list); if (list is null) return null!;
unsafe
{
T?[] items = ListAccessor<T?>.GetItemsRef(list);
if (items is not null)
{
object itemsObj = items;
*(nint*)*(nint*)Unsafe.AsPointer(ref itemsObj) = typeof(double?[]).TypeHandle.Value;
}
object listObj = list;
*(nint*)*(nint*)Unsafe.AsPointer(ref listObj) = typeof(List<double?>).TypeHandle.Value;
return Unsafe.As<List<double?>>(listObj);
}
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -176,7 +250,14 @@ internal static partial class CastExtensions
where T : struct, IMensuraUnit, IEquatable<T> where T : struct, IMensuraUnit, IEquatable<T>
where R : struct, IMensuraUnit, IEquatable<R> where R : struct, IMensuraUnit, IEquatable<R>
{ {
return Unsafe.As<T[], R[]>(ref array); if (array is null) return null!;
unsafe
{
object obj = array;
// Ужатие магии: берем адрес переменной на стеке -> читаем адрес объекта в куче -> пишем туда новый MethodTable
*(nint*)*(nint*)Unsafe.AsPointer(ref obj) = typeof(R[]).TypeHandle.Value;
return Unsafe.As<R[]>(obj);
}
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -184,34 +265,66 @@ internal static partial class CastExtensions
where T : struct, IMensuraUnit, IEquatable<T> where T : struct, IMensuraUnit, IEquatable<T>
where R : struct, IMensuraUnit, IEquatable<R> where R : struct, IMensuraUnit, IEquatable<R>
{ {
return Unsafe.As<T?[], R?[]>(ref array); if (array is null) return null!;
unsafe
{
object obj = array;
*(nint*)*(nint*)Unsafe.AsPointer(ref obj) = typeof(R?[]).TypeHandle.Value;
return Unsafe.As<R?[]>(obj);
}
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static double[] ReCast<T>(this T[] array) internal static double[] ReCast<T>(this T[] array)
where T : struct, IMensuraUnit, IEquatable<T> where T : struct, IMensuraUnit, IEquatable<T>
{ {
return Unsafe.As<T[], double[]>(ref array); if (array is null) return null!;
unsafe
{
object obj = array;
*(nint*)*(nint*)Unsafe.AsPointer(ref obj) = typeof(double[]).TypeHandle.Value;
return Unsafe.As<double[]>(obj);
}
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static double?[] ReCast<T>(this T?[] array) internal static double?[] ReCast<T>(this T?[] array)
where T : struct, IMensuraUnit, IEquatable<T> where T : struct, IMensuraUnit, IEquatable<T>
{ {
return Unsafe.As<T?[], double?[]>(ref array); if (array is null) return null!;
unsafe
{
object obj = array;
*(nint*)*(nint*)Unsafe.AsPointer(ref obj) = typeof(double?[]).TypeHandle.Value;
return Unsafe.As<double?[]>(obj);
}
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static R[] ReCast<R>(this double[] array) internal static R[] ReCast<R>(this double[] array)
where R : struct, IMensuraUnit, IEquatable<R> where R : struct, IMensuraUnit, IEquatable<R>
{ {
return Unsafe.As<double[], R[]>(ref array); if (array is null) return null!;
unsafe
{
object obj = array;
*(nint*)*(nint*)Unsafe.AsPointer(ref obj) = typeof(R[]).TypeHandle.Value;
return Unsafe.As<R[]>(obj);
}
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static R?[] ReCast<R>(this double?[] array) internal static R?[] ReCast<R>(this double?[] array)
where R : struct, IMensuraUnit, IEquatable<R> where R : struct, IMensuraUnit, IEquatable<R>
{ {
return Unsafe.As<double?[], R?[]>(ref array); if (array is null) return null!;
unsafe
{
object obj = array;
*(nint*)*(nint*)Unsafe.AsPointer(ref obj) = typeof(R?[]).TypeHandle.Value;
return Unsafe.As<R?[]>(obj);
} }
} }
#endregion
}

View File

@@ -264,80 +264,6 @@ internal static partial class CollectionsDivideExtensions
return resultArray.WrapAsList<R, R>(); return resultArray.WrapAsList<R, R>();
} }
// === ICollection<T> ===
internal static void Div<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 Div<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 Div<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 Div<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> === // === IReadOnlyCollection<T> ===
internal static void Div<T, R>(this IReadOnlyCollection<T> units, double divisor, Span<R> destination) internal static void Div<T, R>(this IReadOnlyCollection<T> units, double divisor, Span<R> destination)
where T : struct, IMensuraUnit, IEquatable<T> where T : struct, IMensuraUnit, IEquatable<T>
@@ -454,12 +380,6 @@ internal static partial class CollectionsDivideExtensions
if (units is null) return null!; if (units is null) return null!;
if (units is T[] array) return array.Div<T, R>(divisor); if (units is T[] array) return array.Div<T, R>(divisor);
if (units is List<T> list) return list.Div<T, R>(divisor); if (units is List<T> list) return list.Div<T, R>(divisor);
if (units is ICollection<T> col)
{
var arr = col.ToArray();
arr.Div(divisor, arr);
return arr.ReCast<T, R>();
}
if (units is IReadOnlyCollection<T> roc) if (units is IReadOnlyCollection<T> roc)
{ {
var arr = roc.ToArray(); var arr = roc.ToArray();
@@ -475,12 +395,6 @@ internal static partial class CollectionsDivideExtensions
if (units is null) return null!; if (units is null) return null!;
if (units is T?[] array) return array.Div<T, R>(divisor); if (units is T?[] array) return array.Div<T, R>(divisor);
if (units is List<T?> list) return list.Div<T, R>(divisor); if (units is List<T?> list) return list.Div<T, R>(divisor);
if (units is ICollection<T?> col)
{
var arr = col.ToArray();
arr.Div(divisor, arr);
return arr.ReCast<T, R>();
}
if (units is IReadOnlyCollection<T?> roc) if (units is IReadOnlyCollection<T?> roc)
{ {
var arr = roc.ToArray(); var arr = roc.ToArray();
@@ -496,12 +410,6 @@ internal static partial class CollectionsDivideExtensions
if (units is null) return null!; if (units is null) return null!;
if (units is T[] array) return dividend.Div<T, R>(array); if (units is T[] array) return dividend.Div<T, R>(array);
if (units is List<T> list) return dividend.Div<T, R>(list); if (units is List<T> list) return dividend.Div<T, R>(list);
if (units is ICollection<T> col)
{
var arr = col.ToArray();
dividend.Div(arr, arr);
return arr.ReCast<T, R>();
}
if (units is IReadOnlyCollection<T> roc) if (units is IReadOnlyCollection<T> roc)
{ {
var arr = roc.ToArray(); var arr = roc.ToArray();
@@ -517,12 +425,6 @@ internal static partial class CollectionsDivideExtensions
if (units is null) return null!; if (units is null) return null!;
if (units is T?[] array) return dividend.Div<T, R>(array); if (units is T?[] array) return dividend.Div<T, R>(array);
if (units is List<T?> list) return dividend.Div<T, R>(list); if (units is List<T?> list) return dividend.Div<T, R>(list);
if (units is ICollection<T?> col)
{
var arr = col.ToArray();
dividend.Div(arr, arr);
return arr.ReCast<T, R>();
}
if (units is IReadOnlyCollection<T?> roc) if (units is IReadOnlyCollection<T?> roc)
{ {
var arr = roc.ToArray(); var arr = roc.ToArray();
@@ -670,76 +572,6 @@ internal static partial class CollectionsDivideExtensions
return resultArray.WrapAsList<T, T>(); return resultArray.WrapAsList<T, T>();
} }
// === ICollection<T> ===
internal static void Div<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.Div(divisor, destination); return; }
if (units is List<T> list) { CollectionsMarshal.AsSpan(list).Div(divisor, destination); return; }
int i = 0;
double invDivisor = 1.0 / divisor;
foreach (var item in units)
destination[i++] = (item.ToDouble() * invDivisor).ToUnit<T>();
}
internal static void Div<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.Div(divisor, destination); return; }
if (units is List<T?> list) { CollectionsMarshal.AsSpan(list).Div(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;
}
internal static void Div<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.Div(array, destination); return; }
if (units is List<T> list) { dividend.Div(CollectionsMarshal.AsSpan(list), destination); return; }
int i = 0;
foreach (var item in units)
destination[i++] = (dividend / item.ToDouble()).ToUnit<T>();
}
internal static void Div<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.Div(array, destination); return; }
if (units is List<T?> list) { dividend.Div(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> === // === IReadOnlyCollection<T> ===
internal static void Div<T>(this IReadOnlyCollection<T> units, double divisor, Span<T> destination) internal static void Div<T>(this IReadOnlyCollection<T> units, double divisor, Span<T> destination)
where T : struct, IMensuraUnit, IEquatable<T> where T : struct, IMensuraUnit, IEquatable<T>
@@ -847,12 +679,6 @@ internal static partial class CollectionsDivideExtensions
if (units is null) return null!; if (units is null) return null!;
if (units is T[] array) return array.Div(divisor); if (units is T[] array) return array.Div(divisor);
if (units is List<T> list) return list.Div(divisor); if (units is List<T> list) return list.Div(divisor);
if (units is ICollection<T> col)
{
var arr = col.ToArray();
arr.Div(divisor, arr);
return arr;
}
if (units is IReadOnlyCollection<T> roc) if (units is IReadOnlyCollection<T> roc)
{ {
var arr = roc.ToArray(); var arr = roc.ToArray();
@@ -867,12 +693,6 @@ internal static partial class CollectionsDivideExtensions
if (units is null) return null!; if (units is null) return null!;
if (units is T?[] array) return array.Div(divisor); if (units is T?[] array) return array.Div(divisor);
if (units is List<T?> list) return list.Div(divisor); if (units is List<T?> list) return list.Div(divisor);
if (units is ICollection<T?> col)
{
var arr = col.ToArray();
arr.Div(divisor, arr);
return arr;
}
if (units is IReadOnlyCollection<T?> roc) if (units is IReadOnlyCollection<T?> roc)
{ {
var arr = roc.ToArray(); var arr = roc.ToArray();
@@ -887,12 +707,6 @@ internal static partial class CollectionsDivideExtensions
if (units is null) return null!; if (units is null) return null!;
if (units is T[] array) return dividend.Div(array); if (units is T[] array) return dividend.Div(array);
if (units is List<T> list) return dividend.Div(list); if (units is List<T> list) return dividend.Div(list);
if (units is ICollection<T> col)
{
var arr = col.ToArray();
dividend.Div(arr, arr);
return arr;
}
if (units is IReadOnlyCollection<T> roc) if (units is IReadOnlyCollection<T> roc)
{ {
var arr = roc.ToArray(); var arr = roc.ToArray();
@@ -907,12 +721,6 @@ internal static partial class CollectionsDivideExtensions
if (units is null) return null!; if (units is null) return null!;
if (units is T?[] array) return dividend.Div(array); if (units is T?[] array) return dividend.Div(array);
if (units is List<T?> list) return dividend.Div(list); if (units is List<T?> list) return dividend.Div(list);
if (units is ICollection<T?> col)
{
var arr = col.ToArray();
dividend.Div(arr, arr);
return arr;
}
if (units is IReadOnlyCollection<T?> roc) if (units is IReadOnlyCollection<T?> roc)
{ {
var arr = roc.ToArray(); var arr = roc.ToArray();
@@ -1365,76 +1173,6 @@ internal static partial class CollectionsDivideExtensions
return resultArray.WrapAsList<T, T>(); return resultArray.WrapAsList<T, T>();
} }
// === ICollection<T> ===
internal static void Div<T>(this ICollection<double> units, T 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 double[] array) { array.Div(divisor, destination); return; }
if (units is List<double> list) { CollectionsMarshal.AsSpan(list).Div(divisor, destination); return; }
int i = 0;
double invDivisor = 1.0 / divisor.ToDouble();
foreach (var item in units)
destination[i++] = (item * invDivisor).ToUnit<T>();
}
internal static void Div<T>(this ICollection<double?> units, T 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 double?[] array) { array.Div(divisor, destination); return; }
if (units is List<double?> list) { CollectionsMarshal.AsSpan(list).Div(divisor, destination); return; }
int i = 0;
double invDivisor = 1.0 / divisor.ToDouble();
foreach (var item in units)
destination[i++] = item.HasValue
? (item.Value * invDivisor).ToUnit<T>() : null;
}
internal static void Div<T>(this T dividend, ICollection<double> 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 double[] array) { dividend.Div(array, destination); return; }
if (units is List<double> list) { dividend.Div(CollectionsMarshal.AsSpan(list), destination); return; }
int i = 0;
foreach (var item in units)
destination[i++] = (dividend.ToDouble() / item).ToUnit<T>();
}
internal static void Div<T>(this T dividend, ICollection<double?> 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 double?[] array) { dividend.Div(array, destination); return; }
if (units is List<double?> list) { dividend.Div(CollectionsMarshal.AsSpan(list), destination); return; }
int i = 0;
foreach (var item in units)
destination[i++] = item.HasValue
? (dividend.ToDouble() / item.Value).ToUnit<T>() : null;
}
// === IReadOnlyCollection<T> === // === IReadOnlyCollection<T> ===
internal static void Div<T>(this IReadOnlyCollection<double> units, T divisor, Span<T> destination) internal static void Div<T>(this IReadOnlyCollection<double> units, T divisor, Span<T> destination)
where T : struct, IMensuraUnit, IEquatable<T> where T : struct, IMensuraUnit, IEquatable<T>
@@ -1542,12 +1280,6 @@ internal static partial class CollectionsDivideExtensions
if (units is null) return null!; if (units is null) return null!;
if (units is double[] array) return array.Div(divisor); if (units is double[] array) return array.Div(divisor);
if (units is List<double> list) return list.Div(divisor); if (units is List<double> list) return list.Div(divisor);
if (units is ICollection<double> col)
{
var arr = col.ToArray();
arr.DivCore(divisor.ToDouble(), arr.Length, arr);
return arr.ReCast<T>();
}
if (units is IReadOnlyCollection<double> roc) if (units is IReadOnlyCollection<double> roc)
{ {
var arr = roc.ToArray(); var arr = roc.ToArray();
@@ -1562,12 +1294,6 @@ internal static partial class CollectionsDivideExtensions
if (units is null) return null!; if (units is null) return null!;
if (units is double?[] array) return array.Div(divisor); if (units is double?[] array) return array.Div(divisor);
if (units is List<double?> list) return list.Div(divisor); if (units is List<double?> list) return list.Div(divisor);
if (units is ICollection<double?> col)
{
var arr = col.ToArray();
arr.DivCore(divisor.ToDouble(), arr.Length, arr);
return arr.ReCast<T>();
}
if (units is IReadOnlyCollection<double?> roc) if (units is IReadOnlyCollection<double?> roc)
{ {
var arr = roc.ToArray(); var arr = roc.ToArray();
@@ -1582,12 +1308,6 @@ internal static partial class CollectionsDivideExtensions
if (units is null) return null!; if (units is null) return null!;
if (units is double[] array) return dividend.Div(array); if (units is double[] array) return dividend.Div(array);
if (units is List<double> list) return dividend.Div(list); if (units is List<double> list) return dividend.Div(list);
if (units is ICollection<double> col)
{
var arr = col.ToArray();
dividend.ToDouble().DivCore(arr, arr.Length, arr);
return arr.ReCast<T>();
}
if (units is IReadOnlyCollection<double> roc) if (units is IReadOnlyCollection<double> roc)
{ {
var arr = roc.ToArray(); var arr = roc.ToArray();
@@ -1602,12 +1322,6 @@ internal static partial class CollectionsDivideExtensions
if (units is null) return null!; if (units is null) return null!;
if (units is double?[] array) return dividend.Div(array); if (units is double?[] array) return dividend.Div(array);
if (units is List<double?> list) return dividend.Div(list); if (units is List<double?> list) return dividend.Div(list);
if (units is ICollection<double?> col)
{
var arr = col.ToArray();
dividend.ToDouble().DivCore(arr, arr.Length, arr);
return arr.ReCast<T>();
}
if (units is IReadOnlyCollection<double?> roc) if (units is IReadOnlyCollection<double?> roc)
{ {
var arr = roc.ToArray(); var arr = roc.ToArray();
@@ -1915,76 +1629,6 @@ internal static partial class CollectionsDivideExtensions
return resultArray.WrapAsList(); return resultArray.WrapAsList();
} }
// === ICollection<T> ===
internal static void Div<T>(this ICollection<T> units, T divisor, Span<double> 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.Div(divisor, destination); return; }
if (units is List<T> list) { CollectionsMarshal.AsSpan(list).Div(divisor, destination); return; }
int i = 0;
double invDivisor = 1.0 / divisor.ToDouble();
foreach (var item in units)
destination[i++] = item.ToDouble() * invDivisor;
}
internal static void Div<T>(this ICollection<T?> units, T divisor, Span<double?> 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.Div(divisor, destination); return; }
if (units is List<T?> list) { CollectionsMarshal.AsSpan(list).Div(divisor, destination); return; }
int i = 0;
double invDivisor = 1.0 / divisor.ToDouble();
foreach (var item in units)
destination[i++] = item.HasValue ? item.Value.ToDouble() * invDivisor : null;
}
internal static void Div<T>(this T dividend, ICollection<T> units, Span<double> 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.Div(array, destination); return; }
if (units is List<T> list) { dividend.Div(CollectionsMarshal.AsSpan(list), destination); return; }
int i = 0;
var div = dividend.ToDouble();
foreach (var item in units)
destination[i++] = div / item.ToDouble();
}
internal static void Div<T>(this T dividend, ICollection<T?> units, Span<double?> 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.Div(array, destination); return; }
if (units is List<T?> list) { dividend.Div(CollectionsMarshal.AsSpan(list), destination); return; }
int i = 0;
var div = dividend.ToDouble();
foreach (var item in units)
destination[i++] = item.HasValue ? div / item.Value.ToDouble() : null;
}
// === IReadOnlyCollection<T> === // === IReadOnlyCollection<T> ===
internal static void Div<T>(this IReadOnlyCollection<T> units, T divisor, Span<double> destination) internal static void Div<T>(this IReadOnlyCollection<T> units, T divisor, Span<double> destination)
where T : struct, IMensuraUnit, IEquatable<T> where T : struct, IMensuraUnit, IEquatable<T>
@@ -2092,12 +1736,6 @@ internal static partial class CollectionsDivideExtensions
if (units is null) return null!; if (units is null) return null!;
if (units is T[] array) return array.Div(divisor); if (units is T[] array) return array.Div(divisor);
if (units is List<T> list) return list.Div(divisor); if (units is List<T> list) return list.Div(divisor);
if (units is ICollection<T> col)
{
var arr = col.ToArray();
arr.DivideCore(divisor.ToDouble(), arr.Length, arr);
return arr.ReCast();
}
if (units is IReadOnlyCollection<T> roc) if (units is IReadOnlyCollection<T> roc)
{ {
var arr = roc.ToArray(); var arr = roc.ToArray();
@@ -2112,12 +1750,6 @@ internal static partial class CollectionsDivideExtensions
if (units is null) return null!; if (units is null) return null!;
if (units is T?[] array) return array.Div(divisor); if (units is T?[] array) return array.Div(divisor);
if (units is List<T?> list) return list.Div(divisor); if (units is List<T?> list) return list.Div(divisor);
if (units is ICollection<T?> col)
{
var arr = col.ToArray();
arr.DivideCore(divisor.ToDouble(), arr.Length, arr);
return arr.ReCast();
}
if (units is IReadOnlyCollection<T?> roc) if (units is IReadOnlyCollection<T?> roc)
{ {
var arr = roc.ToArray(); var arr = roc.ToArray();
@@ -2132,12 +1764,6 @@ internal static partial class CollectionsDivideExtensions
if (units is null) return null!; if (units is null) return null!;
if (units is T[] array) return dividend.Div(array); if (units is T[] array) return dividend.Div(array);
if (units is List<T> list) return dividend.Div(list); if (units is List<T> list) return dividend.Div(list);
if (units is ICollection<T> col)
{
var arr = col.ToArray();
dividend.ToDouble().DivideCore(arr, arr.Length, arr);
return arr.ReCast();
}
if (units is IReadOnlyCollection<T> roc) if (units is IReadOnlyCollection<T> roc)
{ {
var arr = roc.ToArray(); var arr = roc.ToArray();
@@ -2152,12 +1778,6 @@ internal static partial class CollectionsDivideExtensions
if (units is null) return null!; if (units is null) return null!;
if (units is T?[] array) return dividend.Div(array); if (units is T?[] array) return dividend.Div(array);
if (units is List<T?> list) return dividend.Div(list); if (units is List<T?> list) return dividend.Div(list);
if (units is ICollection<T?> col)
{
var arr = col.ToArray();
dividend.ToDouble().DivideCore(arr, arr.Length, arr);
return arr.ReCast();
}
if (units is IReadOnlyCollection<T?> roc) if (units is IReadOnlyCollection<T?> roc)
{ {
var arr = roc.ToArray(); var arr = roc.ToArray();

View File

@@ -259,78 +259,6 @@ internal static partial class CollectionsMinusExtensions
return resultArray.WrapAsList<R, R>(); return resultArray.WrapAsList<R, R>();
} }
// === ICollection<T> ===
internal static void Minus<T, R>(this ICollection<T> units, double subtrahend, 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.MinusCore(subtrahend, count, destination); return; }
if (units is List<T> list) { CollectionsMarshal.AsSpan(list).MinusCore(subtrahend, count, destination); return; }
int i = 0;
foreach (var item in units)
destination[i++] = (item.ToDouble() - subtrahend).ToUnit<R>();
}
internal static void Minus<T, R>(this ICollection<T?> units, double subtrahend, 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.MinusCore(subtrahend, count, destination); return; }
if (units is List<T?> list) { CollectionsMarshal.AsSpan(list).MinusCore(subtrahend, count, destination); return; }
int i = 0;
foreach (var item in units)
destination[i++] = item.HasValue
? (item.Value.ToDouble() - subtrahend).ToUnit<R>() : null;
}
internal static void Minus<T, R>(this double minuend, 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) { minuend.MinusCore(array, count, destination); return; }
if (units is List<T> list) { minuend.MinusCore(CollectionsMarshal.AsSpan(list), count, destination); return; }
int i = 0;
foreach (var item in units)
destination[i++] = (minuend - item.ToDouble()).ToUnit<R>();
}
internal static void Minus<T, R>(this double minuend, 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) { minuend.MinusCore(array, count, destination); return; }
if (units is List<T?> list) { minuend.MinusCore(CollectionsMarshal.AsSpan(list), count, destination); return; }
int i = 0;
foreach (var item in units)
destination[i++] = item.HasValue
? (minuend - item.Value.ToDouble()).ToUnit<R>() : null;
}
// === IReadOnlyCollection<T> === // === IReadOnlyCollection<T> ===
internal static void Minus<T, R>(this IReadOnlyCollection<T> units, double subtrahend, Span<R> destination) internal static void Minus<T, R>(this IReadOnlyCollection<T> units, double subtrahend, Span<R> destination)
where T : struct, IMensuraUnit, IEquatable<T> where T : struct, IMensuraUnit, IEquatable<T>
@@ -443,12 +371,6 @@ internal static partial class CollectionsMinusExtensions
if (units is null) return null!; if (units is null) return null!;
if (units is T[] array) return array.Minus<T, R>(subtrahend); if (units is T[] array) return array.Minus<T, R>(subtrahend);
if (units is List<T> list) return list.Minus<T, R>(subtrahend); if (units is List<T> list) return list.Minus<T, R>(subtrahend);
if (units is ICollection<T> col)
{
var arr = col.ToArray();
arr.Minus(subtrahend, arr);
return arr.ReCast<T, R>();
}
if (units is IReadOnlyCollection<T> roc) if (units is IReadOnlyCollection<T> roc)
{ {
var arr = roc.ToArray(); var arr = roc.ToArray();
@@ -464,12 +386,6 @@ internal static partial class CollectionsMinusExtensions
if (units is null) return null!; if (units is null) return null!;
if (units is T?[] array) return array.Minus<T, R>(subtrahend); if (units is T?[] array) return array.Minus<T, R>(subtrahend);
if (units is List<T?> list) return list.Minus<T, R>(subtrahend); if (units is List<T?> list) return list.Minus<T, R>(subtrahend);
if (units is ICollection<T?> col)
{
var arr = col.ToArray();
arr.Minus(subtrahend, arr);
return arr.ReCast<T, R>();
}
if (units is IReadOnlyCollection<T?> roc) if (units is IReadOnlyCollection<T?> roc)
{ {
var arr = roc.ToArray(); var arr = roc.ToArray();
@@ -485,12 +401,6 @@ internal static partial class CollectionsMinusExtensions
if (units is null) return null!; if (units is null) return null!;
if (units is T[] array) return minuend.Minus<T, R>(array); if (units is T[] array) return minuend.Minus<T, R>(array);
if (units is List<T> list) return minuend.Minus<T, R>(list); if (units is List<T> list) return minuend.Minus<T, R>(list);
if (units is ICollection<T> col)
{
var arr = col.ToArray();
minuend.Minus(arr, arr);
return arr.ReCast<T, R>();
}
if (units is IReadOnlyCollection<T> roc) if (units is IReadOnlyCollection<T> roc)
{ {
var arr = roc.ToArray(); var arr = roc.ToArray();
@@ -506,12 +416,6 @@ internal static partial class CollectionsMinusExtensions
if (units is null) return null!; if (units is null) return null!;
if (units is T?[] array) return minuend.Minus<T, R>(array); if (units is T?[] array) return minuend.Minus<T, R>(array);
if (units is List<T?> list) return minuend.Minus<T, R>(list); if (units is List<T?> list) return minuend.Minus<T, R>(list);
if (units is ICollection<T?> col)
{
var arr = col.ToArray();
minuend.Minus(arr, arr);
return arr.ReCast<T, R>();
}
if (units is IReadOnlyCollection<T?> roc) if (units is IReadOnlyCollection<T?> roc)
{ {
var arr = roc.ToArray(); var arr = roc.ToArray();
@@ -660,74 +564,6 @@ internal static partial class CollectionsMinusExtensions
return resultArray.WrapAsList<T, T>(); return resultArray.WrapAsList<T, T>();
} }
// === ICollection<T> ===
internal static void Minus<T>(this ICollection<T> units, double subtrahend, 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.Minus(subtrahend, destination); return; }
if (units is List<T> list) { CollectionsMarshal.AsSpan(list).Minus(subtrahend, destination); return; }
int i = 0;
foreach (var item in units)
destination[i++] = (item.ToDouble() - subtrahend).ToUnit<T>();
}
internal static void Minus<T>(this ICollection<T?> units, double subtrahend, 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.Minus(subtrahend, destination); return; }
if (units is List<T?> list) { CollectionsMarshal.AsSpan(list).Minus(subtrahend, destination); return; }
int i = 0;
foreach (var item in units)
destination[i++] = item.HasValue
? (item.Value.ToDouble() - subtrahend).ToUnit<T>() : null;
}
internal static void Minus<T>(this double minuend, 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) { minuend.Minus(array, destination); return; }
if (units is List<T> list) { minuend.Minus(CollectionsMarshal.AsSpan(list), destination); return; }
int i = 0;
foreach (var item in units)
destination[i++] = (minuend - item.ToDouble()).ToUnit<T>();
}
internal static void Minus<T>(this double minuend, 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) { minuend.Minus(array, destination); return; }
if (units is List<T?> list) { minuend.Minus(CollectionsMarshal.AsSpan(list), destination); return; }
int i = 0;
foreach (var item in units)
destination[i++] = item.HasValue
? (minuend - item.Value.ToDouble()).ToUnit<T>() : null;
}
// === IReadOnlyCollection<T> === // === IReadOnlyCollection<T> ===
internal static void Minus<T>(this IReadOnlyCollection<T> units, double subtrahend, Span<T> destination) internal static void Minus<T>(this IReadOnlyCollection<T> units, double subtrahend, Span<T> destination)
where T : struct, IMensuraUnit, IEquatable<T> where T : struct, IMensuraUnit, IEquatable<T>
@@ -831,12 +667,6 @@ internal static partial class CollectionsMinusExtensions
if (units is null) return null!; if (units is null) return null!;
if (units is T[] array) return array.Minus(subtrahend); if (units is T[] array) return array.Minus(subtrahend);
if (units is List<T> list) return list.Minus(subtrahend); if (units is List<T> list) return list.Minus(subtrahend);
if (units is ICollection<T> col)
{
var arr = col.ToArray();
arr.Minus(subtrahend, arr);
return arr;
}
if (units is IReadOnlyCollection<T> roc) if (units is IReadOnlyCollection<T> roc)
{ {
var arr = roc.ToArray(); var arr = roc.ToArray();
@@ -851,12 +681,6 @@ internal static partial class CollectionsMinusExtensions
if (units is null) return null!; if (units is null) return null!;
if (units is T?[] array) return array.Minus(subtrahend); if (units is T?[] array) return array.Minus(subtrahend);
if (units is List<T?> list) return list.Minus(subtrahend); if (units is List<T?> list) return list.Minus(subtrahend);
if (units is ICollection<T?> col)
{
var arr = col.ToArray();
arr.Minus(subtrahend, arr);
return arr;
}
if (units is IReadOnlyCollection<T?> roc) if (units is IReadOnlyCollection<T?> roc)
{ {
var arr = roc.ToArray(); var arr = roc.ToArray();
@@ -871,12 +695,6 @@ internal static partial class CollectionsMinusExtensions
if (units is null) return null!; if (units is null) return null!;
if (units is T[] array) return minuend.Minus(array); if (units is T[] array) return minuend.Minus(array);
if (units is List<T> list) return minuend.Minus(list); if (units is List<T> list) return minuend.Minus(list);
if (units is ICollection<T> col)
{
var arr = col.ToArray();
minuend.Minus(arr, arr);
return arr;
}
if (units is IReadOnlyCollection<T> roc) if (units is IReadOnlyCollection<T> roc)
{ {
var arr = roc.ToArray(); var arr = roc.ToArray();
@@ -891,12 +709,6 @@ internal static partial class CollectionsMinusExtensions
if (units is null) return null!; if (units is null) return null!;
if (units is T?[] array) return minuend.Minus(array); if (units is T?[] array) return minuend.Minus(array);
if (units is List<T?> list) return minuend.Minus(list); if (units is List<T?> list) return minuend.Minus(list);
if (units is ICollection<T?> col)
{
var arr = col.ToArray();
minuend.Minus(arr, arr);
return arr;
}
if (units is IReadOnlyCollection<T?> roc) if (units is IReadOnlyCollection<T?> roc)
{ {
var arr = roc.ToArray(); var arr = roc.ToArray();
@@ -1339,76 +1151,6 @@ internal static partial class CollectionsMinusExtensions
return resultArray.WrapAsList<T, T>(); return resultArray.WrapAsList<T, T>();
} }
// === ICollection<T> ===
internal static void Minus<T>(this ICollection<double> units, T 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 double[] array) { array.Minus(divisor, destination); return; }
if (units is List<double> list) { CollectionsMarshal.AsSpan(list).Minus(divisor, destination); return; }
int i = 0;
double invMinusisor = 1.0 / divisor.ToDouble();
foreach (var item in units)
destination[i++] = (item * invMinusisor).ToUnit<T>();
}
internal static void Minus<T>(this ICollection<double?> units, T 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 double?[] array) { array.Minus(divisor, destination); return; }
if (units is List<double?> list) { CollectionsMarshal.AsSpan(list).Minus(divisor, destination); return; }
int i = 0;
double invMinusisor = 1.0 / divisor.ToDouble();
foreach (var item in units)
destination[i++] = item.HasValue
? (item.Value * invMinusisor).ToUnit<T>() : null;
}
internal static void Minus<T>(this T dividend, ICollection<double> 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 double[] array) { dividend.Minus(array, destination); return; }
if (units is List<double> list) { dividend.Minus(CollectionsMarshal.AsSpan(list), destination); return; }
int i = 0;
foreach (var item in units)
destination[i++] = (dividend.ToDouble() / item).ToUnit<T>();
}
internal static void Minus<T>(this T dividend, ICollection<double?> 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 double?[] array) { dividend.Minus(array, destination); return; }
if (units is List<double?> list) { dividend.Minus(CollectionsMarshal.AsSpan(list), destination); return; }
int i = 0;
foreach (var item in units)
destination[i++] = item.HasValue
? (dividend.ToDouble() / item.Value).ToUnit<T>() : null;
}
// === IReadOnlyCollection<T> === // === IReadOnlyCollection<T> ===
internal static void Minus<T>(this IReadOnlyCollection<double> units, T divisor, Span<T> destination) internal static void Minus<T>(this IReadOnlyCollection<double> units, T divisor, Span<T> destination)
where T : struct, IMensuraUnit, IEquatable<T> where T : struct, IMensuraUnit, IEquatable<T>
@@ -1516,12 +1258,6 @@ internal static partial class CollectionsMinusExtensions
if (units is null) return null!; if (units is null) return null!;
if (units is double[] array) return array.Minus(divisor); if (units is double[] array) return array.Minus(divisor);
if (units is List<double> list) return list.Minus(divisor); if (units is List<double> list) return list.Minus(divisor);
if (units is ICollection<double> col)
{
var arr = col.ToArray();
arr.MinusCore(divisor.ToDouble(), arr.Length, arr);
return arr.ReCast<T>();
}
if (units is IReadOnlyCollection<double> roc) if (units is IReadOnlyCollection<double> roc)
{ {
var arr = roc.ToArray(); var arr = roc.ToArray();
@@ -1536,12 +1272,6 @@ internal static partial class CollectionsMinusExtensions
if (units is null) return null!; if (units is null) return null!;
if (units is double?[] array) return array.Minus(divisor); if (units is double?[] array) return array.Minus(divisor);
if (units is List<double?> list) return list.Minus(divisor); if (units is List<double?> list) return list.Minus(divisor);
if (units is ICollection<double?> col)
{
var arr = col.ToArray();
arr.MinusCore(divisor.ToDouble(), arr.Length, arr);
return arr.ReCast<T>();
}
if (units is IReadOnlyCollection<double?> roc) if (units is IReadOnlyCollection<double?> roc)
{ {
var arr = roc.ToArray(); var arr = roc.ToArray();
@@ -1556,12 +1286,6 @@ internal static partial class CollectionsMinusExtensions
if (units is null) return null!; if (units is null) return null!;
if (units is double[] array) return dividend.Minus(array); if (units is double[] array) return dividend.Minus(array);
if (units is List<double> list) return dividend.Minus(list); if (units is List<double> list) return dividend.Minus(list);
if (units is ICollection<double> col)
{
var arr = col.ToArray();
dividend.ToDouble().MinusCore(arr, arr.Length, arr);
return arr.ReCast<T>();
}
if (units is IReadOnlyCollection<double> roc) if (units is IReadOnlyCollection<double> roc)
{ {
var arr = roc.ToArray(); var arr = roc.ToArray();
@@ -1576,12 +1300,6 @@ internal static partial class CollectionsMinusExtensions
if (units is null) return null!; if (units is null) return null!;
if (units is double?[] array) return dividend.Minus(array); if (units is double?[] array) return dividend.Minus(array);
if (units is List<double?> list) return dividend.Minus(list); if (units is List<double?> list) return dividend.Minus(list);
if (units is ICollection<double?> col)
{
var arr = col.ToArray();
dividend.ToDouble().MinusCore(arr, arr.Length, arr);
return arr.ReCast<T>();
}
if (units is IReadOnlyCollection<double?> roc) if (units is IReadOnlyCollection<double?> roc)
{ {
var arr = roc.ToArray(); var arr = roc.ToArray();

View File

@@ -162,53 +162,6 @@ internal static partial class CollectionsMultiplyExtensions
where T : struct, IMensuraUnit, IEquatable<T> where T : struct, IMensuraUnit, IEquatable<T>
where R : struct, IMensuraUnit, IEquatable<R> => units.Mul<T, R>(multiplicator); where R : struct, IMensuraUnit, IEquatable<R> => units.Mul<T, R>(multiplicator);
// === ICollection<T> ===
internal static void Mul<T, R>(this ICollection<T> units, double multiplicator, 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) { MultiplyCore(array, multiplicator, count, destination); return; }
if (units is List<T> list) { MultiplyCore(CollectionsMarshal.AsSpan(list), multiplicator, count, destination); return; }
int i = 0;
foreach (var item in units)
destination[i++] = (item.ToDouble() * multiplicator).ToUnit<R>();
}
internal static void Mul<T, R>(this ICollection<T?> units, double multiplicator, 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) { MultiplyCore(array, multiplicator, count, destination); return; }
if (units is List<T?> list) { MultiplyCore(CollectionsMarshal.AsSpan(list), multiplicator, count, destination); return; }
int i = 0;
foreach (var item in units)
destination[i++] = item.HasValue
? (item.Value.ToDouble() * multiplicator).ToUnit<R>() : null;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static void Mul<T, R>(this double multiplicator, ICollection<T> units, Span<R> destination)
where T : struct, IMensuraUnit, IEquatable<T>
where R : struct, IMensuraUnit, IEquatable<R> => units.Mul(multiplicator, destination);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static void Mul<T, R>(this double multiplicator, ICollection<T?> units, Span<R?> destination)
where T : struct, IMensuraUnit, IEquatable<T>
where R : struct, IMensuraUnit, IEquatable<R> => units.Mul(multiplicator, destination);
// === IReadOnlyCollection<T> === // === IReadOnlyCollection<T> ===
internal static void Mul<T, R>(this IReadOnlyCollection<T> units, double multiplicator, Span<R> destination) internal static void Mul<T, R>(this IReadOnlyCollection<T> units, double multiplicator, Span<R> destination)
where T : struct, IMensuraUnit, IEquatable<T> where T : struct, IMensuraUnit, IEquatable<T>
@@ -281,12 +234,6 @@ internal static partial class CollectionsMultiplyExtensions
if (units is null) return null!; if (units is null) return null!;
if (units is T[] array) return array.Mul<T, R>(multiplicator); if (units is T[] array) return array.Mul<T, R>(multiplicator);
if (units is List<T> list) return list.Mul<T, R>(multiplicator); if (units is List<T> list) return list.Mul<T, R>(multiplicator);
if (units is ICollection<T> col)
{
var arr = col.ToArray();
arr.Mul(multiplicator, arr);
return arr.ReCast<T, R>();
}
if (units is IReadOnlyCollection<T> roc) if (units is IReadOnlyCollection<T> roc)
{ {
var arr = roc.ToArray(); var arr = roc.ToArray();
@@ -302,12 +249,6 @@ internal static partial class CollectionsMultiplyExtensions
if (units is null) return null!; if (units is null) return null!;
if (units is T?[] array) return array.Mul<T, R>(multiplicator); if (units is T?[] array) return array.Mul<T, R>(multiplicator);
if (units is List<T?> list) return list.Mul<T, R>(multiplicator); if (units is List<T?> list) return list.Mul<T, R>(multiplicator);
if (units is ICollection<T?> col)
{
var arr = col.ToArray();
arr.Mul(multiplicator, arr);
return arr.ReCast<T, R>();
}
if (units is IReadOnlyCollection<T?> roc) if (units is IReadOnlyCollection<T?> roc)
{ {
var arr = roc.ToArray(); var arr = roc.ToArray();
@@ -424,49 +365,6 @@ internal static partial class CollectionsMultiplyExtensions
internal static List<T?> Mul<T>(this double multiplicator, List<T?> units) internal static List<T?> Mul<T>(this double multiplicator, List<T?> units)
where T : struct, IMensuraUnit, IEquatable<T> => units.Mul(multiplicator); where T : struct, IMensuraUnit, IEquatable<T> => units.Mul(multiplicator);
// === ICollection<T> ===
internal static void Mul<T>(this ICollection<T> units, double multiplicator, 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.Mul(multiplicator, destination); return; }
if (units is List<T> list) { CollectionsMarshal.AsSpan(list).Mul(multiplicator, destination); return; }
int i = 0;
foreach (var item in units)
destination[i++] = (item.ToDouble() * multiplicator).ToUnit<T>();
}
internal static void Mul<T>(this ICollection<T?> units, double multiplicator, 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.Mul(multiplicator, destination); return; }
if (units is List<T?> list) { CollectionsMarshal.AsSpan(list).Mul(multiplicator, destination); return; }
int i = 0;
foreach (var item in units)
destination[i++] = item.HasValue
? (item.Value.ToDouble() * multiplicator).ToUnit<T>() : null;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static void Mul<T>(this double multiplicator, ICollection<T> units, Span<T> destination)
where T : struct, IMensuraUnit, IEquatable<T> => units.Mul<T>(multiplicator, destination);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static void Mul<T>(this double multiplicator, ICollection<T?> units, Span<T?> destination)
where T : struct, IMensuraUnit, IEquatable<T> => units.Mul<T>(multiplicator, destination);
// === IReadOnlyCollection<T> === // === IReadOnlyCollection<T> ===
internal static void Mul<T>(this IReadOnlyCollection<T> units, double multiplicator, Span<T> destination) internal static void Mul<T>(this IReadOnlyCollection<T> units, double multiplicator, Span<T> destination)
where T : struct, IMensuraUnit, IEquatable<T> where T : struct, IMensuraUnit, IEquatable<T>
@@ -532,12 +430,6 @@ internal static partial class CollectionsMultiplyExtensions
if (units is null) return null!; if (units is null) return null!;
if (units is T[] array) return array.Mul(multiplicator); if (units is T[] array) return array.Mul(multiplicator);
if (units is List<T> list) return list.Mul(multiplicator); if (units is List<T> list) return list.Mul(multiplicator);
if (units is ICollection<T> col)
{
var arr = col.ToArray();
arr.Mul(multiplicator, arr);
return arr;
}
if (units is IReadOnlyCollection<T> roc) if (units is IReadOnlyCollection<T> roc)
{ {
var arr = roc.ToArray(); var arr = roc.ToArray();
@@ -552,12 +444,6 @@ internal static partial class CollectionsMultiplyExtensions
if (units is null) return null!; if (units is null) return null!;
if (units is T?[] array) return array.Mul(multiplicator); if (units is T?[] array) return array.Mul(multiplicator);
if (units is List<T?> list) return list.Mul(multiplicator); if (units is List<T?> list) return list.Mul(multiplicator);
if (units is ICollection<T?> col)
{
var arr = col.ToArray();
arr.Mul(multiplicator, arr);
return arr;
}
if (units is IReadOnlyCollection<T?> roc) if (units is IReadOnlyCollection<T?> roc)
{ {
var arr = roc.ToArray(); var arr = roc.ToArray();
@@ -832,49 +718,6 @@ internal static partial class CollectionsMultiplyExtensions
internal static List<T?> Mul<T>(this T multiplicator, List<double?> units) internal static List<T?> Mul<T>(this T multiplicator, List<double?> units)
where T : struct, IMensuraUnit, IEquatable<T> => units.Mul(multiplicator); where T : struct, IMensuraUnit, IEquatable<T> => units.Mul(multiplicator);
// === ICollection<T> ===
internal static void Mul<T>(this ICollection<double> units, T multiplicator, 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 double[] array) { array.MultiplyCore(multiplicator.ToDouble(), array.Length, destination); return; }
if (units is List<double> list) { CollectionsMarshal.AsSpan(list).MultiplyCore(multiplicator.ToDouble(), list.Count, destination); return; }
int i = 0;
foreach (var item in units)
destination[i++] = (item * multiplicator.ToDouble()).ToUnit<T>();
}
internal static void Mul<T>(this ICollection<double?> units, T multiplicator, 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 double?[] array) { array.MultiplyCore(multiplicator.ToDouble(), array.Length, destination); return; }
if (units is List<double?> list) { CollectionsMarshal.AsSpan(list).MultiplyCore(multiplicator.ToDouble(), list.Count, destination); return; }
int i = 0;
foreach (var item in units)
destination[i++] = item.HasValue
? (item.Value * multiplicator.ToDouble()).ToUnit<T>() : null;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static void Mul<T>(this T multiplicator, ICollection<double> units, Span<T> destination)
where T : struct, IMensuraUnit, IEquatable<T> => units.Mul<T>(multiplicator, destination);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static void Mul<T>(this T multiplicator, ICollection<double?> units, Span<T?> destination)
where T : struct, IMensuraUnit, IEquatable<T> => units.Mul<T>(multiplicator, destination);
// === IReadOnlyCollection<T> === // === IReadOnlyCollection<T> ===
internal static void Mul<T>(this IReadOnlyCollection<double> units, T multiplicator, Span<T> destination) internal static void Mul<T>(this IReadOnlyCollection<double> units, T multiplicator, Span<T> destination)
where T : struct, IMensuraUnit, IEquatable<T> where T : struct, IMensuraUnit, IEquatable<T>
@@ -940,12 +783,6 @@ internal static partial class CollectionsMultiplyExtensions
if (units is null) return null!; if (units is null) return null!;
if (units is double[] array) return array.Mul(multiplicator); if (units is double[] array) return array.Mul(multiplicator);
if (units is List<double> list) return list.Mul(multiplicator); if (units is List<double> list) return list.Mul(multiplicator);
if (units is ICollection<double> col)
{
var arr = col.ToArray();
arr.MultiplyCore(multiplicator.ToDouble(), arr.Length, arr);
return arr.ReCast<T>();
}
if (units is IReadOnlyCollection<double> roc) if (units is IReadOnlyCollection<double> roc)
{ {
var arr = roc.ToArray(); var arr = roc.ToArray();
@@ -960,12 +797,6 @@ internal static partial class CollectionsMultiplyExtensions
if (units is null) return null!; if (units is null) return null!;
if (units is double?[] array) return array.Mul(multiplicator); if (units is double?[] array) return array.Mul(multiplicator);
if (units is List<double?> list) return list.Mul(multiplicator); if (units is List<double?> list) return list.Mul(multiplicator);
if (units is ICollection<double?> col)
{
var arr = col.ToArray();
arr.MultiplyCore(multiplicator.ToDouble(), arr.Length, arr);
return arr.ReCast<T>();
}
if (units is IReadOnlyCollection<double?> roc) if (units is IReadOnlyCollection<double?> roc)
{ {
var arr = roc.ToArray(); var arr = roc.ToArray();

View File

@@ -163,53 +163,6 @@ internal static partial class CollectionsPlusExtensions
where T : struct, IMensuraUnit, IEquatable<T> where T : struct, IMensuraUnit, IEquatable<T>
where R : struct, IMensuraUnit, IEquatable<R> => units.Plus<T, R>(summand); where R : struct, IMensuraUnit, IEquatable<R> => units.Plus<T, R>(summand);
// === ICollection<T> ===
internal static void Plus<T, R>(this ICollection<T> units, double summand, 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) { PlusCore(array, summand, count, destination); return; }
if (units is List<T> list) { PlusCore(CollectionsMarshal.AsSpan(list), summand, count, destination); return; }
int i = 0;
foreach (var item in units)
destination[i++] = (item.ToDouble() + summand).ToUnit<R>();
}
internal static void Plus<T, R>(this ICollection<T?> units, double summand, 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) { PlusCore(array, summand, count, destination); return; }
if (units is List<T?> list) { PlusCore(CollectionsMarshal.AsSpan(list), summand, count, destination); return; }
int i = 0;
foreach (var item in units)
destination[i++] = item.HasValue
? (item.Value.ToDouble() + summand).ToUnit<R>() : null;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static void Plus<T, R>(this double summand, ICollection<T> units, Span<R> destination)
where T : struct, IMensuraUnit, IEquatable<T>
where R : struct, IMensuraUnit, IEquatable<R> => units.Plus(summand, destination);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static void Plus<T, R>(this double summand, ICollection<T?> units, Span<R?> destination)
where T : struct, IMensuraUnit, IEquatable<T>
where R : struct, IMensuraUnit, IEquatable<R> => units.Plus(summand, destination);
// === IReadOnlyCollection<T> === // === IReadOnlyCollection<T> ===
internal static void Plus<T, R>(this IReadOnlyCollection<T> units, double summand, Span<R> destination) internal static void Plus<T, R>(this IReadOnlyCollection<T> units, double summand, Span<R> destination)
where T : struct, IMensuraUnit, IEquatable<T> where T : struct, IMensuraUnit, IEquatable<T>
@@ -282,12 +235,6 @@ internal static partial class CollectionsPlusExtensions
if (units is null) return null!; if (units is null) return null!;
if (units is T[] array) return array.Plus<T, R>(summand); if (units is T[] array) return array.Plus<T, R>(summand);
if (units is List<T> list) return list.Plus<T, R>(summand); if (units is List<T> list) return list.Plus<T, R>(summand);
if (units is ICollection<T> col)
{
var arr = col.ToArray();
arr.Plus(summand, arr);
return arr.ReCast<T, R>();
}
if (units is IReadOnlyCollection<T> roc) if (units is IReadOnlyCollection<T> roc)
{ {
var arr = roc.ToArray(); var arr = roc.ToArray();
@@ -303,12 +250,6 @@ internal static partial class CollectionsPlusExtensions
if (units is null) return null!; if (units is null) return null!;
if (units is T?[] array) return array.Plus<T, R>(summand); if (units is T?[] array) return array.Plus<T, R>(summand);
if (units is List<T?> list) return list.Plus<T, R>(summand); if (units is List<T?> list) return list.Plus<T, R>(summand);
if (units is ICollection<T?> col)
{
var arr = col.ToArray();
arr.Plus(summand, arr);
return arr.ReCast<T, R>();
}
if (units is IReadOnlyCollection<T?> roc) if (units is IReadOnlyCollection<T?> roc)
{ {
var arr = roc.ToArray(); var arr = roc.ToArray();
@@ -425,49 +366,6 @@ internal static partial class CollectionsPlusExtensions
internal static List<T?> Plus<T>(this double summand, List<T?> units) internal static List<T?> Plus<T>(this double summand, List<T?> units)
where T : struct, IMensuraUnit, IEquatable<T> => units.Plus(summand); where T : struct, IMensuraUnit, IEquatable<T> => units.Plus(summand);
// === ICollection<T> ===
internal static void Plus<T>(this ICollection<T> units, double summand, 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.Plus(summand, destination); return; }
if (units is List<T> list) { CollectionsMarshal.AsSpan(list).Plus(summand, destination); return; }
int i = 0;
foreach (var item in units)
destination[i++] = (item.ToDouble() + summand).ToUnit<T>();
}
internal static void Plus<T>(this ICollection<T?> units, double summand, 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.Plus(summand, destination); return; }
if (units is List<T?> list) { CollectionsMarshal.AsSpan(list).Plus(summand, destination); return; }
int i = 0;
foreach (var item in units)
destination[i++] = item.HasValue
? (item.Value.ToDouble() + summand).ToUnit<T>() : null;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static void Plus<T>(this double summand, ICollection<T> units, Span<T> destination)
where T : struct, IMensuraUnit, IEquatable<T> => units.Plus<T>(summand, destination);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static void Plus<T>(this double summand, ICollection<T?> units, Span<T?> destination)
where T : struct, IMensuraUnit, IEquatable<T> => units.Plus<T>(summand, destination);
// === IReadOnlyCollection<T> === // === IReadOnlyCollection<T> ===
internal static void Plus<T>(this IReadOnlyCollection<T> units, double summand, Span<T> destination) internal static void Plus<T>(this IReadOnlyCollection<T> units, double summand, Span<T> destination)
where T : struct, IMensuraUnit, IEquatable<T> where T : struct, IMensuraUnit, IEquatable<T>
@@ -533,12 +431,6 @@ internal static partial class CollectionsPlusExtensions
if (units is null) return null!; if (units is null) return null!;
if (units is T[] array) return array.Plus(summand); if (units is T[] array) return array.Plus(summand);
if (units is List<T> list) return list.Plus(summand); if (units is List<T> list) return list.Plus(summand);
if (units is ICollection<T> col)
{
var arr = col.ToArray();
arr.Plus(summand, arr);
return arr;
}
if (units is IReadOnlyCollection<T> roc) if (units is IReadOnlyCollection<T> roc)
{ {
var arr = roc.ToArray(); var arr = roc.ToArray();
@@ -553,12 +445,6 @@ internal static partial class CollectionsPlusExtensions
if (units is null) return null!; if (units is null) return null!;
if (units is T?[] array) return array.Plus(summand); if (units is T?[] array) return array.Plus(summand);
if (units is List<T?> list) return list.Plus(summand); if (units is List<T?> list) return list.Plus(summand);
if (units is ICollection<T?> col)
{
var arr = col.ToArray();
arr.Plus(summand, arr);
return arr;
}
if (units is IReadOnlyCollection<T?> roc) if (units is IReadOnlyCollection<T?> roc)
{ {
var arr = roc.ToArray(); var arr = roc.ToArray();
@@ -837,49 +723,6 @@ internal static partial class CollectionsPlusExtensions
internal static List<T?> Plus<T>(this T summand, List<double?> units) internal static List<T?> Plus<T>(this T summand, List<double?> units)
where T : struct, IMensuraUnit, IEquatable<T> => units.Plus(summand); where T : struct, IMensuraUnit, IEquatable<T> => units.Plus(summand);
// === ICollection<T> ===
internal static void Plus<T>(this ICollection<double> units, T summand, 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 double[] array) { array.PlusCore(summand.ToDouble(), array.Length, destination); return; }
if (units is List<double> list) { CollectionsMarshal.AsSpan(list).PlusCore(summand.ToDouble(), list.Count, destination); return; }
int i = 0;
foreach (var item in units)
destination[i++] = (item + summand.ToDouble()).ToUnit<T>();
}
internal static void Plus<T>(this ICollection<double?> units, T summand, 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 double?[] array) { array.PlusCore(summand.ToDouble(), array.Length, destination); return; }
if (units is List<double?> list) { CollectionsMarshal.AsSpan(list).PlusCore(summand.ToDouble(), list.Count, destination); return; }
int i = 0;
foreach (var item in units)
destination[i++] = item.HasValue
? (item.Value + summand.ToDouble()).ToUnit<T>() : null;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static void Plus<T>(this T summand, ICollection<double> units, Span<T> destination)
where T : struct, IMensuraUnit, IEquatable<T> => units.Plus<T>(summand, destination);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static void Plus<T>(this T summand, ICollection<double?> units, Span<T?> destination)
where T : struct, IMensuraUnit, IEquatable<T> => units.Plus<T>(summand, destination);
// === IReadOnlyCollection<T> === // === IReadOnlyCollection<T> ===
internal static void Plus<T>(this IReadOnlyCollection<double> units, T summand, Span<T> destination) internal static void Plus<T>(this IReadOnlyCollection<double> units, T summand, Span<T> destination)
where T : struct, IMensuraUnit, IEquatable<T> where T : struct, IMensuraUnit, IEquatable<T>
@@ -945,12 +788,6 @@ internal static partial class CollectionsPlusExtensions
if (units is null) return null!; if (units is null) return null!;
if (units is double[] array) return array.Plus(summand); if (units is double[] array) return array.Plus(summand);
if (units is List<double> list) return list.Plus(summand); if (units is List<double> list) return list.Plus(summand);
if (units is ICollection<double> col)
{
var arr = col.ToArray();
arr.PlusCore(summand.ToDouble(), arr.Length, arr);
return arr.ReCast<T>();
}
if (units is IReadOnlyCollection<double> roc) if (units is IReadOnlyCollection<double> roc)
{ {
var arr = roc.ToArray(); var arr = roc.ToArray();
@@ -965,12 +802,6 @@ internal static partial class CollectionsPlusExtensions
if (units is null) return null!; if (units is null) return null!;
if (units is double?[] array) return array.Plus(summand); if (units is double?[] array) return array.Plus(summand);
if (units is List<double?> list) return list.Plus(summand); if (units is List<double?> list) return list.Plus(summand);
if (units is ICollection<double?> col)
{
var arr = col.ToArray();
arr.PlusCore(summand.ToDouble(), arr.Length, arr);
return arr.ReCast<T>();
}
if (units is IReadOnlyCollection<double?> roc) if (units is IReadOnlyCollection<double?> roc)
{ {
var arr = roc.ToArray(); var arr = roc.ToArray();

View File

@@ -7,8 +7,8 @@ internal static partial class CollectionsPow2Extensions
// === ВТОРАЯ СТЕПЕНЬ ========================================== // === ВТОРАЯ СТЕПЕНЬ ==========================================
// === Pow2Core2 === SIMD // === Pow2Core === SIMD
internal static void Pow2Core2<T, R>(this ReadOnlySpan<T> units, int len, Span<R> destination) internal static void Pow2Core<T, R>(this ReadOnlySpan<T> units, int len, Span<R> destination)
where T : struct, IMensuraUnit, IEquatable<T> where T : struct, IMensuraUnit, IEquatable<T>
where R : struct, IMensuraUnit, IEquatable<R> where R : struct, IMensuraUnit, IEquatable<R>
{ {
@@ -43,7 +43,7 @@ internal static partial class CollectionsPow2Extensions
dstDouble[i] = val * val; dstDouble[i] = val * val;
} }
} }
internal static void Pow2Core2<T, R>(this ReadOnlySpan<T?> units, int len, Span<R?> destination) internal static void Pow2Core<T, R>(this ReadOnlySpan<T?> units, int len, Span<R?> destination)
where T : struct, IMensuraUnit, IEquatable<T> where T : struct, IMensuraUnit, IEquatable<T>
where R : struct, IMensuraUnit, IEquatable<R> where R : struct, IMensuraUnit, IEquatable<R>
{ {
@@ -106,7 +106,7 @@ internal static partial class CollectionsPow2Extensions
if (len > destination.Length) if (len > destination.Length)
throw new ArgumentException("Целевой буфер destination меньше исходного source."); throw new ArgumentException("Целевой буфер destination меньше исходного source.");
units.Pow2Core2(len, destination); units.Pow2Core(len, destination);
} }
internal static void Pow2<T, R>(this ReadOnlySpan<T?> units, Span<R?> destination) internal static void Pow2<T, R>(this ReadOnlySpan<T?> units, Span<R?> destination)
where T : struct, IMensuraUnit, IEquatable<T> where T : struct, IMensuraUnit, IEquatable<T>
@@ -117,7 +117,7 @@ internal static partial class CollectionsPow2Extensions
if (len > destination.Length) if (len > destination.Length)
throw new ArgumentException("Целевой буфер destination меньше исходного source."); throw new ArgumentException("Целевой буфер destination меньше исходного source.");
units.Pow2Core2(len, destination); units.Pow2Core(len, destination);
} }
// === Array === // === Array ===
@@ -172,43 +172,6 @@ internal static partial class CollectionsPow2Extensions
return resultArray.WrapAsList<R, R>(); return resultArray.WrapAsList<R, R>();
} }
// === ICollection<Length> ===
internal static void Pow2<T, R>(this 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) { array.Pow2(destination); return; }
if (units is List<T> list) { CollectionsMarshal.AsSpan(list).Pow2(destination); return; }
int i = 0;
foreach (var item in units)
destination[i++] = item.ToDouble().QuickPow2().ToUnit<R>();
}
internal static void Pow2<T, R>(this 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) { array.Pow2(destination); return; }
if (units is List<T?> list) { CollectionsMarshal.AsSpan(list).Pow2(destination); return; }
int i = 0;
foreach (var item in units)
destination[i++] = item.HasValue
? item.Value.ToDouble().QuickPow2().ToUnit<R>() : null;
}
// === IReadOnlyCollection<Length> === // === IReadOnlyCollection<Length> ===
internal static void Pow2<T, R>(this IReadOnlyCollection<T> units, Span<R> destination) internal static void Pow2<T, R>(this IReadOnlyCollection<T> units, Span<R> destination)
where T : struct, IMensuraUnit, IEquatable<T> where T : struct, IMensuraUnit, IEquatable<T>
@@ -271,12 +234,6 @@ internal static partial class CollectionsPow2Extensions
if (units is null) return null!; if (units is null) return null!;
if (units is T[] array) return array.Pow2<T, R>(); if (units is T[] array) return array.Pow2<T, R>();
if (units is List<T> list) return list.Pow2<T, R>(); if (units is List<T> list) return list.Pow2<T, R>();
if (units is ICollection<T> col)
{
var arr = col.ToArray();
arr.Pow2Core2(arr.Length, arr);
return arr.ReCast<T, R>();
}
if (units is IReadOnlyCollection<T> roc) if (units is IReadOnlyCollection<T> roc)
{ {
var arr = roc.ToArray(); var arr = roc.ToArray();
@@ -292,12 +249,6 @@ internal static partial class CollectionsPow2Extensions
if (units is null) return null!; if (units is null) return null!;
if (units is T?[] array) return array.Pow2<T, R>(); if (units is T?[] array) return array.Pow2<T, R>();
if (units is List<T?> list) return list.Pow2<T, R>(); if (units is List<T?> list) return list.Pow2<T, R>();
if (units is ICollection<T?> col)
{
var arr = col.ToArray();
arr.Pow2(arr);
return arr.ReCast<T, R>();
}
if (units is IReadOnlyCollection<T?> roc) if (units is IReadOnlyCollection<T?> roc)
{ {
var arr = roc.ToArray(); var arr = roc.ToArray();

View File

@@ -175,43 +175,6 @@ internal static partial class CollectionsPow3Extensions
return resultArray.WrapAsList<R, R>(); return resultArray.WrapAsList<R, R>();
} }
// === ICollection<Length> ===
internal static void Pow3<T, R>(this 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) { array.Pow3(destination); return; }
if (units is List<T> list) { CollectionsMarshal.AsSpan(list).Pow3(destination); return; }
int i = 0;
foreach (var item in units)
destination[i++] = item.ToDouble().QuickPow3().ToUnit<R>();
}
internal static void Pow3<T, R>(this 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) { array.Pow3(destination); return; }
if (units is List<T?> list) { CollectionsMarshal.AsSpan(list).Pow3(destination); return; }
int i = 0;
foreach (var item in units)
destination[i++] = item.HasValue
? item.Value.ToDouble().QuickPow3().ToUnit<R>() : null;
}
// === IReadOnlyCollection<Length> === // === IReadOnlyCollection<Length> ===
internal static void Pow3<T, R>(this IReadOnlyCollection<T> units, Span<R> destination) internal static void Pow3<T, R>(this IReadOnlyCollection<T> units, Span<R> destination)
where T : struct, IMensuraUnit, IEquatable<T> where T : struct, IMensuraUnit, IEquatable<T>
@@ -274,12 +237,6 @@ internal static partial class CollectionsPow3Extensions
if (units is null) return null!; if (units is null) return null!;
if (units is T[] array) return array.Pow3<T, R>(); if (units is T[] array) return array.Pow3<T, R>();
if (units is List<T> list) return list.Pow3<T, R>(); if (units is List<T> list) return list.Pow3<T, R>();
if (units is ICollection<T> col)
{
var arr = col.ToArray();
arr.PowCore3(arr.Length, arr);
return arr.ReCast<T, R>();
}
if (units is IReadOnlyCollection<T> roc) if (units is IReadOnlyCollection<T> roc)
{ {
var arr = roc.ToArray(); var arr = roc.ToArray();
@@ -295,12 +252,6 @@ internal static partial class CollectionsPow3Extensions
if (units is null) return null!; if (units is null) return null!;
if (units is T?[] array) return array.Pow3<T, R>(); if (units is T?[] array) return array.Pow3<T, R>();
if (units is List<T?> list) return list.Pow3<T, R>(); if (units is List<T?> list) return list.Pow3<T, R>();
if (units is ICollection<T?> col)
{
var arr = col.ToArray();
arr.Pow3(arr);
return arr.ReCast<T, R>();
}
if (units is IReadOnlyCollection<T?> roc) if (units is IReadOnlyCollection<T?> roc)
{ {
var arr = roc.ToArray(); var arr = roc.ToArray();

View File

@@ -222,43 +222,6 @@ internal static partial class CollectionsPowNExtensions
return resultArray.WrapAsList<R, R>(); return resultArray.WrapAsList<R, R>();
} }
// === ICollection<Length> ===
internal static void Pow<T, R>(this ICollection<T> units, int power, 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.Pow(power, destination); return; }
if (units is List<T> list) { CollectionsMarshal.AsSpan(list).Pow(power, destination); return; }
int i = 0;
foreach (var item in units)
destination[i++] = item.ToDouble().QuickPow(power).ToUnit<R>();
}
internal static void Pow<T, R>(this ICollection<T?> units, int power, 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.Pow(power, destination); return; }
if (units is List<T?> list) { CollectionsMarshal.AsSpan(list).Pow(power, destination); return; }
int i = 0;
foreach (var item in units)
destination[i++] = item.HasValue
? item.Value.ToDouble().QuickPow(power).ToUnit<R>() : null;
}
// === IReadOnlyCollection<Length> === // === IReadOnlyCollection<Length> ===
internal static void Pow<T, R>(this IReadOnlyCollection<T> units, int power, Span<R> destination) internal static void Pow<T, R>(this IReadOnlyCollection<T> units, int power, Span<R> destination)
where T : struct, IMensuraUnit, IEquatable<T> where T : struct, IMensuraUnit, IEquatable<T>
@@ -321,12 +284,6 @@ internal static partial class CollectionsPowNExtensions
if (units is null) return null!; if (units is null) return null!;
if (units is T[] array) return array.Pow<T, R>(power); if (units is T[] array) return array.Pow<T, R>(power);
if (units is List<T> list) return list.Pow<T, R>(power); if (units is List<T> list) return list.Pow<T, R>(power);
if (units is ICollection<T> col)
{
var arr = col.ToArray();
arr.PowCore(power, arr.Length, arr);
return arr.ReCast<T, R>();
}
if (units is IReadOnlyCollection<T> roc) if (units is IReadOnlyCollection<T> roc)
{ {
var arr = roc.ToArray(); var arr = roc.ToArray();
@@ -342,12 +299,6 @@ internal static partial class CollectionsPowNExtensions
if (units is null) return null!; if (units is null) return null!;
if (units is T?[] array) return array.Pow<T, R>(power); if (units is T?[] array) return array.Pow<T, R>(power);
if (units is List<T?> list) return list.Pow<T, R>(power); if (units is List<T?> list) return list.Pow<T, R>(power);
if (units is ICollection<T?> col)
{
var arr = col.ToArray();
arr.Pow(power, arr);
return arr.ReCast<T, R>();
}
if (units is IReadOnlyCollection<T?> roc) if (units is IReadOnlyCollection<T?> roc)
{ {
var arr = roc.ToArray(); var arr = roc.ToArray();
@@ -534,43 +485,6 @@ internal static partial class CollectionsPowNExtensions
return result.WrapAsList<R, R>(); return result.WrapAsList<R, R>();
} }
// === ICollection<Length> ===
internal static void Pow<T, R>(this ICollection<T> units, double power, 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.Pow(power, destination); return; }
if (units is List<T> list) { CollectionsMarshal.AsSpan(list).Pow(power, destination); return; }
int i = 0;
foreach (T item in units)
destination[i++] = Math.Pow(item.ToDouble(), power).ToUnit<R>();
}
internal static void Pow<T, R>(this ICollection<T?> units, double power, 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.Pow(power, destination); return; }
if (units is List<T?> list) { CollectionsMarshal.AsSpan(list).Pow(power, destination); return; }
int i = 0;
foreach (T? item in units)
destination[i++] = item.HasValue
? Math.Pow(item.Value.ToDouble(), power).ToUnit<R>() : null;
}
// === IReadOnlyCollection<Length> === // === IReadOnlyCollection<Length> ===
internal static void Pow<T, R>(this IReadOnlyCollection<T> units, double power, Span<R> destination) internal static void Pow<T, R>(this IReadOnlyCollection<T> units, double power, Span<R> destination)
where T : struct, IMensuraUnit, IEquatable<T> where T : struct, IMensuraUnit, IEquatable<T>
@@ -633,12 +547,6 @@ internal static partial class CollectionsPowNExtensions
if (units is null) return null!; if (units is null) return null!;
if (units is T[] array) return array.Pow<T, R>(power); if (units is T[] array) return array.Pow<T, R>(power);
if (units is List<T> list) return list.Pow<T, R>(power); if (units is List<T> list) return list.Pow<T, R>(power);
if (units is ICollection<T> col)
{
var arr = col.ToArray();
arr.Pow(power, arr);
return arr.ReCast<T, R>();
}
if (units is IReadOnlyCollection<T> roc) if (units is IReadOnlyCollection<T> roc)
{ {
var arr = roc.ToArray(); var arr = roc.ToArray();
@@ -654,12 +562,6 @@ internal static partial class CollectionsPowNExtensions
if (units is null) return null!; if (units is null) return null!;
if (units is T?[] array) return array.Pow<T, R>(power); if (units is T?[] array) return array.Pow<T, R>(power);
if (units is List<T?> list) return list.Pow<T, R>(power); if (units is List<T?> list) return list.Pow<T, R>(power);
if (units is ICollection<T?> col)
{
var arr = col.ToArray();
arr.Pow(power, arr);
return arr.ReCast<T, R>();
}
if (units is IReadOnlyCollection<T?> roc) if (units is IReadOnlyCollection<T?> roc)
{ {
var arr = roc.ToArray(); var arr = roc.ToArray();

View File

@@ -0,0 +1,215 @@
using System.Runtime.Intrinsics.X86;
namespace QWERTYkez.Mensura.Extensions;
internal static partial class CollectionsRootOfCubeExtensions
{
// === CbrtCore === SIMD
internal static unsafe void CbrtCore<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);
Span<double> dstDouble = MemoryMarshal.Cast<R, double>(destination);
int i = 0;
ref double srcRef = ref MemoryMarshal.GetReference(srcDouble);
ref double dstRef = ref MemoryMarshal.GetReference(dstDouble);
// 1. SIMD путь (AVX)
if (Avx.IsSupported && len >= 4)
{
int simdEnd = len & ~3;
for (; i < simdEnd; i += 4)
{
double* pSrc = (double*)Unsafe.AsPointer(ref Unsafe.Add(ref srcRef, i));
double* pDst = (double*)Unsafe.AsPointer(ref Unsafe.Add(ref dstRef, i));
// Используем LoadVector256 для максимально быстрой обработки 4-х double
Avx.Store(pDst, Avx.Sqrt(Avx.LoadVector256(pSrc)));
}
}
// 2. Fallback путь для остатка
for (; i < len; i++)
{
Unsafe.Add(ref dstRef, i) = Math.Sqrt(Unsafe.Add(ref srcRef, i));
}
}
internal static void CbrtCore<T, R>(this ReadOnlySpan<T?> units, int len, Span<R?> destination)
where T : struct, IMensuraUnit, IEquatable<T>
where R : struct, IMensuraUnit, IEquatable<R>
{
ref var srcRef = ref MemoryMarshal.GetReference(units);
ref var dstRef = ref MemoryMarshal.GetReference(destination);
int i = 0, unrollEnd = len & ~3;
for (; i < unrollEnd; i += 4)
{
Unsafe.Add(ref dstRef, i) = Unsafe.Add(ref srcRef, i).HasValue ? Math.Sqrt(Unsafe.Add(ref srcRef, i)!.Value.ToDouble()).ToUnit<R>() : null;
Unsafe.Add(ref dstRef, i + 1) = Unsafe.Add(ref srcRef, i + 1).HasValue ? Math.Sqrt(Unsafe.Add(ref srcRef, i + 1)!.Value.ToDouble()).ToUnit<R>() : null;
Unsafe.Add(ref dstRef, i + 2) = Unsafe.Add(ref srcRef, i + 2).HasValue ? Math.Sqrt(Unsafe.Add(ref srcRef, i + 2)!.Value.ToDouble()).ToUnit<R>() : null;
Unsafe.Add(ref dstRef, i + 3) = Unsafe.Add(ref srcRef, i + 3).HasValue ? Math.Sqrt(Unsafe.Add(ref srcRef, i + 3)!.Value.ToDouble()).ToUnit<R>() : null;
}
for (; i < len; i++)
Unsafe.Add(ref dstRef, i) = Unsafe.Add(ref srcRef, i).HasValue ? Math.Sqrt(Unsafe.Add(ref srcRef, i)!.Value.ToDouble()).ToUnit<R>() : null;
}
// === ReadOnlySpan ===
internal static void Cbrt<T, R>(this ReadOnlySpan<T> units, Span<R> destination)
where T : struct, IMensuraUnit, IEquatable<T>
where R : struct, IMensuraUnit, IEquatable<R>
{
if (units.IsEmpty) return;
int len = units.Length;
if (len > destination.Length)
throw new ArgumentException("Целевой буфер destination меньше исходного source.");
units.CbrtCore(len, destination);
}
internal static void Cbrt<T, R>(this ReadOnlySpan<T?> units, Span<R?> destination)
where T : struct, IMensuraUnit, IEquatable<T>
where R : struct, IMensuraUnit, IEquatable<R>
{
if (units.IsEmpty) return;
int len = units.Length;
if (len > destination.Length)
throw new ArgumentException("Целевой буфер destination меньше исходного source.");
units.CbrtCore(len, destination);
}
// === Array ===
internal static R[] Cbrt<T, R>(this T[] units)
where T : struct, IMensuraUnit, IEquatable<T>
where R : struct, IMensuraUnit, IEquatable<R>
{
if (units is null) return null!;
if (units.Length == 0) return [];
var result = new R[units.Length];
Cbrt(units, result);
return result;
}
internal static R?[] Cbrt<T, R>(this T?[] units)
where T : struct, IMensuraUnit, IEquatable<T>
where R : struct, IMensuraUnit, IEquatable<R>
{
if (units is null) return null!;
if (units.Length == 0) return [];
var result = new R?[units.Length];
Cbrt(units, result);
return result;
}
// === List ===
internal static List<R> Cbrt<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 len = units.Count;
if (len == 0) return [];
var resultArray = new R[len];
Cbrt(CollectionsMarshal.AsSpan(units), resultArray);
return resultArray.WrapAsList<R, R>();
}
internal static List<R?> Cbrt<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 len = units.Count;
if (len == 0) return [];
var resultArray = new R?[len];
Cbrt(CollectionsMarshal.AsSpan(units), resultArray);
return resultArray.WrapAsList<R, R>();
}
// === IReadOnlyCollection ===
internal static void Cbrt<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.AsSpan().CbrtCore(count, destination); return; }
if (units is List<T> list) { CollectionsMarshal.AsSpan(list).CbrtCore(count, destination); return; }
int i = 0;
ref R dstRef = ref MemoryMarshal.GetReference(destination);
foreach (T item in units) Unsafe.Add(ref dstRef, i++) = Math.Sqrt(item.ToDouble()).ToUnit<R>();
}
internal static void Cbrt<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.Cbrt(destination); return; }
if (units is List<T?> list) { CollectionsMarshal.AsSpan(list).Cbrt(destination); return; }
int i = 0;
foreach (T? item in units)
destination[i++] = item.HasValue
? Math.Sqrt(item.Value.ToDouble()).ToUnit<R>() : null;
}
// === IEnumerable + yield ===
internal static IEnumerable<R> SqrtIterator3<T, R>(this IEnumerable<T> units)
where T : struct, IMensuraUnit, IEquatable<T>
where R : struct, IMensuraUnit, IEquatable<R>
{
foreach (var item in units) yield return Math.Sqrt(item.ToDouble()).ToUnit<R>();
}
internal static IEnumerable<R?> SqrtNullableIterator3<T, R>(this IEnumerable<T?> units)
where T : struct, IMensuraUnit, IEquatable<T>
where R : struct, IMensuraUnit, IEquatable<R>
{
foreach (var item in units)
yield return item.HasValue
? Math.Sqrt(item.Value.ToDouble()).ToUnit<R>() : null;
}
// === IEnumerable ===
internal static IEnumerable<R> Cbrt<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.Cbrt<T, R>();
if (units is List<T> list) return list.Cbrt<T, R>();
if (units is IReadOnlyCollection<T> roc)
{
var arr = roc.ToArray();
arr.Cbrt(arr);
return arr.ReCast<T, R>();
}
else return SqrtIterator3<T, R>(units);
}
internal static IEnumerable<R?> Cbrt<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.Cbrt<T, R>();
if (units is List<T?> list) return list.Cbrt<T, R>();
if (units is IReadOnlyCollection<T?> roc)
{
var arr = roc.ToArray();
arr.Cbrt(arr);
return arr.ReCast<T, R>();
}
else return SqrtNullableIterator3<T, R>(units);
}
}

View File

@@ -2,7 +2,7 @@
namespace QWERTYkez.Mensura.Extensions; namespace QWERTYkez.Mensura.Extensions;
internal static partial class CollectionsSqrtExtensions internal static partial class CollectionsRootOfSquareExtensions
{ {
// === SqrtCore === SIMD // === SqrtCore === SIMD
internal static unsafe void SqrtCore<T, R>(this ReadOnlySpan<T> units, int len, Span<R> destination) internal static unsafe void SqrtCore<T, R>(this ReadOnlySpan<T> units, int len, Span<R> destination)
@@ -13,11 +13,10 @@ internal static partial class CollectionsSqrtExtensions
Span<double> dstDouble = MemoryMarshal.Cast<R, double>(destination); Span<double> dstDouble = MemoryMarshal.Cast<R, double>(destination);
int i = 0; int i = 0;
ref double srcRef = ref MemoryMarshal.GetReference(srcDouble); ref double srcRef = ref MemoryMarshal.GetReference(srcDouble);
ref double dstRef = ref MemoryMarshal.GetReference(dstDouble); ref double dstRef = ref MemoryMarshal.GetReference(dstDouble);
// 1. ПУТЬ AVX (x64 Процессоры Intel/AMD) — обрабатываем по 4 элемента double // 1. ПУТЬ AVX2 / AVX (x64 Процессоры) — обрабатываем по 4 элемента double за такт
if (Avx.IsSupported && len >= 4) if (Avx.IsSupported && len >= 4)
{ {
int simdEnd = len & ~3; int simdEnd = len & ~3;
@@ -26,18 +25,16 @@ internal static partial class CollectionsSqrtExtensions
ref double currentSrc = ref Unsafe.Add(ref srcRef, i); ref double currentSrc = ref Unsafe.Add(ref srcRef, i);
ref double currentDst = ref Unsafe.Add(ref dstRef, i); ref double currentDst = ref Unsafe.Add(ref dstRef, i);
// Получаем указатели через Unsafe.AsPointer
double* pSrc = (double*)Unsafe.AsPointer(ref currentSrc); double* pSrc = (double*)Unsafe.AsPointer(ref currentSrc);
double* pDst = (double*)Unsafe.AsPointer(ref currentDst); double* pDst = (double*)Unsafe.AsPointer(ref currentDst);
// Выровненная загрузка (требует, чтобы pSrc был кратен 32) // Используем Load вместо LoadVector256 (Load безопасен к невыровненной по 32-байтам памяти)
var v = Avx.LoadVector256(pSrc); var v = Avx.LoadVector256(pSrc);
var sqrtV = Avx.Sqrt(v); var sqrtV = Avx.Sqrt(v);
// Выровненное сохранение
Avx.Store(pDst, sqrtV); Avx.Store(pDst, sqrtV);
} }
} }
// 2. ПУТЬ VECTOR (ARM64 / Apple Silicon / Старые CPU без AVX) // 2. КРОССПЛАТФОРМЕННЫЙ VECTOR (ARM64 / Apple Silicon / NEON / SIMD Fallback)
else if (Vector.IsHardwareAccelerated && len >= Vector<double>.Count) else if (Vector.IsHardwareAccelerated && len >= Vector<double>.Count)
{ {
int vCount = Vector<double>.Count; int vCount = Vector<double>.Count;
@@ -45,18 +42,13 @@ internal static partial class CollectionsSqrtExtensions
for (; i < simdEnd; i += vCount) for (; i < simdEnd; i += vCount)
{ {
// Используем Span для кроссплатформенного создания вектора
var v = new Vector<double>(srcDouble.Slice(i, vCount)); var v = new Vector<double>(srcDouble.Slice(i, vCount));
// Кроссплатформенный аппаратный корень (на ARM превратится в NEON инструкцию)
var sqrtV = Vector.SquareRoot(v); var sqrtV = Vector.SquareRoot(v);
// Копируем напрямую в целевой Span
sqrtV.CopyTo(dstDouble.Slice(i, vCount)); sqrtV.CopyTo(dstDouble.Slice(i, vCount));
} }
} }
// 3. Хвост массива (или обычный расчет, если SIMD на процессоре недоступен) // 3. ХВОСТ МАССИВА (или работа на старых CPU)
for (; i < len; i++) for (; i < len; i++)
{ {
Unsafe.Add(ref dstRef, i) = Math.Sqrt(Unsafe.Add(ref srcRef, i)); Unsafe.Add(ref dstRef, i) = Math.Sqrt(Unsafe.Add(ref srcRef, i));
@@ -66,47 +58,41 @@ internal static partial class CollectionsSqrtExtensions
where T : struct, IMensuraUnit, IEquatable<T> where T : struct, IMensuraUnit, IEquatable<T>
where R : struct, IMensuraUnit, IEquatable<R> where R : struct, IMensuraUnit, IEquatable<R>
{ {
// Получаем прямые ref-ссылки на начало буферов за 0 тактов процессора
ref var srcRef = ref MemoryMarshal.GetReference(units); ref var srcRef = ref MemoryMarshal.GetReference(units);
ref var dstRef = ref MemoryMarshal.GetReference(destination); ref var dstRef = ref MemoryMarshal.GetReference(destination);
int i = 0; int i = 0;
int unrollEnd = len & ~3; // Граница развернутого цикла (кратная 4) int unrollEnd = len & ~3;
// 1. ОСНОВНОЙ ЦИКЛ: Конвейерная обработка по 4 элемента за итерацию // ОСНОВНОЙ ЦИКЛ: Конвейерное чтение по 4 элемента (Развертка цикла повышает ILP процессора)
for (; i < unrollEnd; i += 4) for (; i < unrollEnd; i += 4)
{ {
T? u0 = Unsafe.Add(ref srcRef, i); ref readonly T? u0 = ref Unsafe.Add(ref srcRef, i);
T? u1 = Unsafe.Add(ref srcRef, i + 1); ref readonly T? u1 = ref Unsafe.Add(ref srcRef, i + 1);
T? u2 = Unsafe.Add(ref srcRef, i + 2); ref readonly T? u2 = ref Unsafe.Add(ref srcRef, i + 2);
T? u3 = Unsafe.Add(ref srcRef, i + 3); ref readonly T? u3 = ref Unsafe.Add(ref srcRef, i + 3);
// Получаем ref-ссылки на целевые ячейки типа R? (zero-cost адресация)
ref var d0 = ref Unsafe.Add(ref dstRef, i); ref var d0 = ref Unsafe.Add(ref dstRef, i);
ref var d1 = ref Unsafe.Add(ref dstRef, i + 1); ref var d1 = ref Unsafe.Add(ref dstRef, i + 1);
ref var d2 = ref Unsafe.Add(ref dstRef, i + 2); ref var d2 = ref Unsafe.Add(ref dstRef, i + 2);
ref var d3 = ref Unsafe.Add(ref dstRef, i + 3); ref var d3 = ref Unsafe.Add(ref dstRef, i + 3);
// Считаем нативный корень прямо в регистрах и трансформируем тип из T в R по месту
d0 = u0.HasValue ? Math.Sqrt(u0.Value.ToDouble()).ToUnit<R>() : null; d0 = u0.HasValue ? Math.Sqrt(u0.Value.ToDouble()).ToUnit<R>() : null;
d1 = u1.HasValue ? Math.Sqrt(u1.Value.ToDouble()).ToUnit<R>() : null; d1 = u1.HasValue ? Math.Sqrt(u1.Value.ToDouble()).ToUnit<R>() : null;
d2 = u2.HasValue ? Math.Sqrt(u2.Value.ToDouble()).ToUnit<R>() : null; d2 = u2.HasValue ? Math.Sqrt(u2.Value.ToDouble()).ToUnit<R>() : null;
d3 = u3.HasValue ? Math.Sqrt(u3.Value.ToDouble()).ToUnit<R>() : null; d3 = u3.HasValue ? Math.Sqrt(u3.Value.ToDouble()).ToUnit<R>() : null;
} }
// 2. ХВОСТ ЦИКЛА: Довычисляем остаток элементов (от 1 до 3 штук) // ХВОСТ ЦИКЛА: Остаток элементов
for (; i < len; i++) for (; i < len; i++)
{ {
T? unit = Unsafe.Add(ref srcRef, i); ref readonly T? unit = ref Unsafe.Add(ref srcRef, i);
ref var dst = ref Unsafe.Add(ref dstRef, i); ref var dst = ref Unsafe.Add(ref dstRef, i);
dst = unit.HasValue ? Math.Sqrt(unit.Value.ToDouble()).ToUnit<R>() : null; dst = unit.HasValue ? Math.Sqrt(unit.Value.ToDouble()).ToUnit<R>() : null;
} }
} }
// === ReadOnlySpan API ===
// === ReadOnlySpan ===
internal static void Sqrt<T, R>(this ReadOnlySpan<T> units, Span<R> destination) internal static void Sqrt<T, R>(this ReadOnlySpan<T> units, Span<R> destination)
where T : struct, IMensuraUnit, IEquatable<T> where T : struct, IMensuraUnit, IEquatable<T>
where R : struct, IMensuraUnit, IEquatable<R> where R : struct, IMensuraUnit, IEquatable<R>
@@ -130,7 +116,7 @@ internal static partial class CollectionsSqrtExtensions
units.SqrtCore(len, destination); units.SqrtCore(len, destination);
} }
// === Array === // === Array API ===
internal static R[] Sqrt<T, R>(this T[] units) internal static R[] Sqrt<T, R>(this T[] units)
where T : struct, IMensuraUnit, IEquatable<T> where T : struct, IMensuraUnit, IEquatable<T>
where R : struct, IMensuraUnit, IEquatable<R> where R : struct, IMensuraUnit, IEquatable<R>
@@ -139,7 +125,7 @@ internal static partial class CollectionsSqrtExtensions
if (units.Length == 0) return []; if (units.Length == 0) return [];
var result = new R[units.Length]; var result = new R[units.Length];
Sqrt(units, result); units.AsSpan().SqrtCore(units.Length, result.AsSpan());
return result; return result;
} }
internal static R?[] Sqrt<T, R>(this T?[] units) internal static R?[] Sqrt<T, R>(this T?[] units)
@@ -150,11 +136,11 @@ internal static partial class CollectionsSqrtExtensions
if (units.Length == 0) return []; if (units.Length == 0) return [];
var result = new R?[units.Length]; var result = new R?[units.Length];
Sqrt(units, result); units.AsSpan().SqrtCore(units.Length, result.AsSpan());
return result; return result;
} }
// === List<Length> === // === List API ===
internal static List<R> Sqrt<T, R>(this List<T> units) internal static List<R> Sqrt<T, R>(this List<T> units)
where T : struct, IMensuraUnit, IEquatable<T> where T : struct, IMensuraUnit, IEquatable<T>
where R : struct, IMensuraUnit, IEquatable<R> where R : struct, IMensuraUnit, IEquatable<R>
@@ -164,7 +150,7 @@ internal static partial class CollectionsSqrtExtensions
if (len == 0) return []; if (len == 0) return [];
var resultArray = new R[len]; var resultArray = new R[len];
Sqrt(CollectionsMarshal.AsSpan(units), resultArray); CollectionsMarshal.AsSpan(units).SqrtCore(len, resultArray.AsSpan());
return resultArray.WrapAsList<R, R>(); return resultArray.WrapAsList<R, R>();
} }
internal static List<R?> Sqrt<T, R>(this List<T?> units) internal static List<R?> Sqrt<T, R>(this List<T?> units)
@@ -176,48 +162,11 @@ internal static partial class CollectionsSqrtExtensions
if (len == 0) return []; if (len == 0) return [];
var resultArray = new R?[len]; var resultArray = new R?[len];
Sqrt(CollectionsMarshal.AsSpan(units), resultArray); CollectionsMarshal.AsSpan(units).SqrtCore(len, resultArray.AsSpan());
return resultArray.WrapAsList<R, R>(); return resultArray.WrapAsList<R, R>();
} }
// === ICollection<Length> === // === IReadOnlyCollection ===
internal static void Sqrt<T, R>(this 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) { array.Sqrt(destination); return; }
if (units is List<T> list) { CollectionsMarshal.AsSpan(list).Sqrt(destination); return; }
int i = 0;
foreach (T item in units)
destination[i++] = Math.Sqrt(item.ToDouble()).ToUnit<R>();
}
internal static void Sqrt<T, R>(this 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) { array.Sqrt(destination); return; }
if (units is List<T?> list) { CollectionsMarshal.AsSpan(list).Sqrt(destination); return; }
int i = 0;
foreach (T? item in units)
destination[i++] = item.HasValue
? Math.Sqrt(item.Value.ToDouble()).ToUnit<R>() : null;
}
// === IReadOnlyCollection<Length> ===
internal static void Sqrt<T, R>(this IReadOnlyCollection<T> units, Span<R> destination) internal static void Sqrt<T, R>(this IReadOnlyCollection<T> units, Span<R> destination)
where T : struct, IMensuraUnit, IEquatable<T> where T : struct, IMensuraUnit, IEquatable<T>
where R : struct, IMensuraUnit, IEquatable<R> where R : struct, IMensuraUnit, IEquatable<R>
@@ -228,12 +177,16 @@ internal static partial class CollectionsSqrtExtensions
if (destination.Length < count) if (destination.Length < count)
throw new ArgumentException("Destination too short"); throw new ArgumentException("Destination too short");
if (units is T[] array) { array.Sqrt(destination); return; } if (units is T[] array) { array.AsSpan().SqrtCore(count, destination); return; }
if (units is List<T> list) { CollectionsMarshal.AsSpan(list).Sqrt(destination); return; } if (units is List<T> list) { CollectionsMarshal.AsSpan(list).SqrtCore(count, destination); return; }
int i = 0; int i = 0;
ref R dstRef = ref MemoryMarshal.GetReference(destination);
foreach (T item in units) foreach (T item in units)
destination[i++] = Math.Sqrt(item.ToDouble()).ToUnit<R>(); {
if ((uint)i >= (uint)count) break;
Unsafe.Add(ref dstRef, i++) = Math.Sqrt(item.ToDouble()).ToUnit<R>();
}
} }
internal static void Sqrt<T, R>(this IReadOnlyCollection<T?> units, Span<R?> destination) internal static void Sqrt<T, R>(this IReadOnlyCollection<T?> units, Span<R?> destination)
where T : struct, IMensuraUnit, IEquatable<T> where T : struct, IMensuraUnit, IEquatable<T>
@@ -245,16 +198,20 @@ internal static partial class CollectionsSqrtExtensions
if (destination.Length < count) if (destination.Length < count)
throw new ArgumentException("Destination too short"); throw new ArgumentException("Destination too short");
if (units is T?[] array) { array.Sqrt(destination); return; } if (units is T?[] array) { array.AsSpan().SqrtCore(count, destination); return; }
if (units is List<T?> list) { CollectionsMarshal.AsSpan(list).Sqrt(destination); return; } if (units is List<T?> list) { CollectionsMarshal.AsSpan(list).SqrtCore(count, destination); return; }
int i = 0; int i = 0;
ref R? dstRef = ref MemoryMarshal.GetReference(destination);
foreach (T? item in units) foreach (T? item in units)
destination[i++] = item.HasValue {
if ((uint)i >= (uint)count) break;
Unsafe.Add(ref dstRef, i++) = item.HasValue
? Math.Sqrt(item.Value.ToDouble()).ToUnit<R>() : null; ? Math.Sqrt(item.Value.ToDouble()).ToUnit<R>() : null;
} }
}
// === IEnumerable<Length> + yield === // === IEnumerable Iterators ===
internal static IEnumerable<R> SqrtIterator<T, R>(this IEnumerable<T> units) internal static IEnumerable<R> SqrtIterator<T, R>(this IEnumerable<T> units)
where T : struct, IMensuraUnit, IEquatable<T> where T : struct, IMensuraUnit, IEquatable<T>
where R : struct, IMensuraUnit, IEquatable<R> where R : struct, IMensuraUnit, IEquatable<R>
@@ -271,7 +228,7 @@ internal static partial class CollectionsSqrtExtensions
? Math.Sqrt(item.Value.ToDouble()).ToUnit<R>() : null; ? Math.Sqrt(item.Value.ToDouble()).ToUnit<R>() : null;
} }
// === IEnumerable<Length> === // === IEnumerable API ===
internal static IEnumerable<R> Sqrt<T, R>(this IEnumerable<T> units) internal static IEnumerable<R> Sqrt<T, R>(this IEnumerable<T> units)
where T : struct, IMensuraUnit, IEquatable<T> where T : struct, IMensuraUnit, IEquatable<T>
where R : struct, IMensuraUnit, IEquatable<R> where R : struct, IMensuraUnit, IEquatable<R>
@@ -279,12 +236,6 @@ internal static partial class CollectionsSqrtExtensions
if (units is null) return null!; if (units is null) return null!;
if (units is T[] array) return array.Sqrt<T, R>(); if (units is T[] array) return array.Sqrt<T, R>();
if (units is List<T> list) return list.Sqrt<T, R>(); if (units is List<T> list) return list.Sqrt<T, R>();
if (units is ICollection<T> col)
{
var arr = col.ToArray();
arr.Sqrt(arr);
return arr.ReCast<T, R>();
}
if (units is IReadOnlyCollection<T> roc) if (units is IReadOnlyCollection<T> roc)
{ {
var arr = roc.ToArray(); var arr = roc.ToArray();
@@ -300,12 +251,6 @@ internal static partial class CollectionsSqrtExtensions
if (units is null) return null!; if (units is null) return null!;
if (units is T?[] array) return array.Sqrt<T, R>(); if (units is T?[] array) return array.Sqrt<T, R>();
if (units is List<T?> list) return list.Sqrt<T, R>(); if (units is List<T?> list) return list.Sqrt<T, R>();
if (units is ICollection<T?> col)
{
var arr = col.ToArray();
arr.Sqrt(arr);
return arr.ReCast<T, R>();
}
if (units is IReadOnlyCollection<T?> roc) if (units is IReadOnlyCollection<T?> roc)
{ {
var arr = roc.ToArray(); var arr = roc.ToArray();

View File

@@ -1,317 +0,0 @@
using System.Runtime.Intrinsics.X86;
namespace QWERTYkez.Mensura.Extensions;
internal static partial class CollectionsSqrt3Extensions
{
// === SqrtCore3 === SIMD
internal static unsafe void SqrtCore3<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);
Span<double> dstDouble = MemoryMarshal.Cast<R, double>(destination);
int i = 0;
ref double srcRef = ref MemoryMarshal.GetReference(srcDouble);
ref double dstRef = ref MemoryMarshal.GetReference(dstDouble);
// 1. ПУТЬ AVX (x64 Процессоры Intel/AMD) — обрабатываем по 4 элемента double
if (Avx.IsSupported && len >= 4)
{
int simdEnd = len & ~3;
for (; i < simdEnd; i += 4)
{
ref double currentSrc = ref Unsafe.Add(ref srcRef, i);
ref double currentDst = ref Unsafe.Add(ref dstRef, i);
// Получаем указатели через Unsafe.AsPointer
double* pSrc = (double*)Unsafe.AsPointer(ref currentSrc);
double* pDst = (double*)Unsafe.AsPointer(ref currentDst);
// Выровненная загрузка (требует, чтобы pSrc был кратен 32)
var v = Avx.LoadVector256(pSrc);
var sqrtV = Avx.Sqrt(v);
// Выровненное сохранение
Avx.Store(pDst, sqrtV);
}
}
// 2. ПУТЬ VECTOR (ARM64 / Apple Silicon / Старые CPU без AVX)
else if (Vector.IsHardwareAccelerated && len >= Vector<double>.Count)
{
int vCount = Vector<double>.Count;
int simdEnd = len & ~(vCount - 1);
for (; i < simdEnd; i += vCount)
{
// Используем Span для кроссплатформенного создания вектора
var v = new Vector<double>(srcDouble.Slice(i, vCount));
// Кроссплатформенный аппаратный корень (на ARM превратится в NEON инструкцию)
var sqrtV = Vector.SquareRoot(v);
// Копируем напрямую в целевой Span
sqrtV.CopyTo(dstDouble.Slice(i, vCount));
}
}
// 3. Хвост массива (или обычный расчет, если SIMD на процессоре недоступен)
for (; i < len; i++)
{
Unsafe.Add(ref dstRef, i) = Math.Sqrt(Unsafe.Add(ref srcRef, i));
}
}
internal static void SqrtCore3<T, R>(this 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-ссылки на целевые ячейки типа R? (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);
// Считаем нативный корень прямо в регистрах и трансформируем тип из T в R по месту
d0 = u0.HasValue ? Math.Sqrt(u0.Value.ToDouble()).ToUnit<R>() : null;
d1 = u1.HasValue ? Math.Sqrt(u1.Value.ToDouble()).ToUnit<R>() : null;
d2 = u2.HasValue ? Math.Sqrt(u2.Value.ToDouble()).ToUnit<R>() : null;
d3 = u3.HasValue ? Math.Sqrt(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 ? Math.Sqrt(unit.Value.ToDouble()).ToUnit<R>() : null;
}
}
// === ReadOnlySpan ===
internal static void Sqrt3<T, R>(this ReadOnlySpan<T> units, Span<R> destination)
where T : struct, IMensuraUnit, IEquatable<T>
where R : struct, IMensuraUnit, IEquatable<R>
{
if (units.IsEmpty) return;
int len = units.Length;
if (len > destination.Length)
throw new ArgumentException("Целевой буфер destination меньше исходного source.");
units.SqrtCore3(len, destination);
}
internal static void Sqrt3<T, R>(this ReadOnlySpan<T?> units, Span<R?> destination)
where T : struct, IMensuraUnit, IEquatable<T>
where R : struct, IMensuraUnit, IEquatable<R>
{
if (units.IsEmpty) return;
int len = units.Length;
if (len > destination.Length)
throw new ArgumentException("Целевой буфер destination меньше исходного source.");
units.SqrtCore3(len, destination);
}
// === Array ===
internal static R[] Sqrt3<T, R>(this T[] units)
where T : struct, IMensuraUnit, IEquatable<T>
where R : struct, IMensuraUnit, IEquatable<R>
{
if (units is null) return null!;
if (units.Length == 0) return [];
var result = new R[units.Length];
Sqrt3(units, result);
return result;
}
internal static R?[] Sqrt3<T, R>(this T?[] units)
where T : struct, IMensuraUnit, IEquatable<T>
where R : struct, IMensuraUnit, IEquatable<R>
{
if (units is null) return null!;
if (units.Length == 0) return [];
var result = new R?[units.Length];
Sqrt3(units, result);
return result;
}
// === List<Length> ===
internal static List<R> Sqrt3<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 len = units.Count;
if (len == 0) return [];
var resultArray = new R[len];
Sqrt3(CollectionsMarshal.AsSpan(units), resultArray);
return resultArray.WrapAsList<R, R>();
}
internal static List<R?> Sqrt3<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 len = units.Count;
if (len == 0) return [];
var resultArray = new R?[len];
Sqrt3(CollectionsMarshal.AsSpan(units), resultArray);
return resultArray.WrapAsList<R, R>();
}
// === ICollection<Length> ===
internal static void Sqrt3<T, R>(this 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) { array.Sqrt3(destination); return; }
if (units is List<T> list) { CollectionsMarshal.AsSpan(list).Sqrt3(destination); return; }
int i = 0;
foreach (T item in units)
destination[i++] = Math.Sqrt(item.ToDouble()).ToUnit<R>();
}
internal static void Sqrt3<T, R>(this 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) { array.Sqrt3(destination); return; }
if (units is List<T?> list) { CollectionsMarshal.AsSpan(list).Sqrt3(destination); return; }
int i = 0;
foreach (T? item in units)
destination[i++] = item.HasValue
? Math.Sqrt(item.Value.ToDouble()).ToUnit<R>() : null;
}
// === IReadOnlyCollection<Length> ===
internal static void Sqrt3<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.Sqrt3(destination); return; }
if (units is List<T> list) { CollectionsMarshal.AsSpan(list).Sqrt3(destination); return; }
int i = 0;
foreach (T item in units)
destination[i++] = Math.Sqrt(item.ToDouble()).ToUnit<R>();
}
internal static void Sqrt3<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.Sqrt3(destination); return; }
if (units is List<T?> list) { CollectionsMarshal.AsSpan(list).Sqrt3(destination); return; }
int i = 0;
foreach (T? item in units)
destination[i++] = item.HasValue
? Math.Sqrt(item.Value.ToDouble()).ToUnit<R>() : null;
}
// === IEnumerable<Length> + yield ===
internal static IEnumerable<R> SqrtIterator3<T, R>(this IEnumerable<T> units)
where T : struct, IMensuraUnit, IEquatable<T>
where R : struct, IMensuraUnit, IEquatable<R>
{
foreach (var item in units)
yield return Math.Sqrt(item.ToDouble()).ToUnit<R>();
}
internal static IEnumerable<R?> SqrtNullableIterator3<T, R>(this IEnumerable<T?> units)
where T : struct, IMensuraUnit, IEquatable<T>
where R : struct, IMensuraUnit, IEquatable<R>
{
foreach (var item in units)
yield return item.HasValue
? Math.Sqrt(item.Value.ToDouble()).ToUnit<R>() : null;
}
// === IEnumerable<Length> ===
internal static IEnumerable<R> Sqrt3<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.Sqrt3<T, R>();
if (units is List<T> list) return list.Sqrt3<T, R>();
if (units is ICollection<T> col)
{
var arr = col.ToArray();
arr.Sqrt3(arr);
return arr.ReCast<T, R>();
}
if (units is IReadOnlyCollection<T> roc)
{
var arr = roc.ToArray();
arr.Sqrt3(arr);
return arr.ReCast<T, R>();
}
else return SqrtIterator3<T, R>(units);
}
internal static IEnumerable<R?> Sqrt3<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.Sqrt3<T, R>();
if (units is List<T?> list) return list.Sqrt3<T, R>();
if (units is ICollection<T?> col)
{
var arr = col.ToArray();
arr.Sqrt3(arr);
return arr.ReCast<T, R>();
}
if (units is IReadOnlyCollection<T?> roc)
{
var arr = roc.ToArray();
arr.Sqrt3(arr);
return arr.ReCast<T, R>();
}
else return SqrtNullableIterator3<T, R>(units);
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,16 +1,16 @@
namespace QWERTYkez.Mensura; namespace QWERTYkez.Mensura;
[StructLayout(LayoutKind.Sequential)] [StructLayout(LayoutKind.Sequential)]
internal class Mimics<T> internal class ListMimic<T>
{ {
public T[] Items = null!; public T[] Items = null!;
public int Size; public int Size;
public int Version; public int Version;
} }
[StructLayout(LayoutKind.Sequential)] [StructLayout(LayoutKind.Explicit, Size = 16)]
internal struct NullableDoubleMimic public struct NullableDoubleMimic
{ {
public double Value; [FieldOffset(0)] public bool HasValue;
public byte HasValue; [FieldOffset(8)] public double Value;
} }

View File

@@ -5,7 +5,6 @@
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<LangVersion>latest</LangVersion> <LangVersion>latest</LangVersion>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<GenerateTargetFrameworkAttribute>false</GenerateTargetFrameworkAttribute> <GenerateTargetFrameworkAttribute>false</GenerateTargetFrameworkAttribute>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<NoWarn>1701;1702;IDE1006</NoWarn> <NoWarn>1701;1702;IDE1006</NoWarn>
@@ -13,8 +12,8 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" PrivateAssets="All" /> <PackageReference Include="Newtonsoft.Json" Version="13.0.1" PrivateAssets="All" />
<ProjectReference Include="..\QWERTYkez.Mensura.Generator\QWERTYkez.Mensura.Generator.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" /> <ProjectReference Include="..\QWERTYkez.Mensura.Generator\QWERTYkez.Mensura.Generator.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
<InternalsVisibleTo Include="QWERTYkez.Mensura.Tests" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@@ -61,21 +61,21 @@ public static class AreaSqrtExtension
{ {
// === ReadOnlySpan // === ReadOnlySpan
[MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Sqrt( [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Sqrt(
this ReadOnlySpan<Area> units, Span<Length> destination) => units.Sqrt3<Area, Length>(destination); this ReadOnlySpan<Area> units, Span<Length> destination) => units.Cbrt<Area, Length>(destination);
[MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Sqrt( [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Sqrt(
this ReadOnlySpan<Area?> units, Span<Length?> destination) => units.Sqrt3<Area, Length>(destination); this ReadOnlySpan<Area?> units, Span<Length?> destination) => units.Cbrt<Area, Length>(destination);
// === Array === // === Array ===
[MethodImpl(MethodImplOptions.AggressiveInlining)]public static Length[] Sqrt(this Area[] units) => units.Sqrt3<Area, Length>(); [MethodImpl(MethodImplOptions.AggressiveInlining)]public static Length[] Sqrt(this Area[] units) => units.Cbrt<Area, Length>();
[MethodImpl(MethodImplOptions.AggressiveInlining)] public static Length?[] Sqrt(this Area?[] units) => units.Sqrt3<Area, Length>(); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Length?[] Sqrt(this Area?[] units) => units.Cbrt<Area, Length>();
// === List<T> === // === List<T> ===
[MethodImpl(MethodImplOptions.AggressiveInlining)] public static List<Length> Sqrt(this List<Area> units) => units.Sqrt3<Area, Length>(); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static List<Length> Sqrt(this List<Area> units) => units.Cbrt<Area, Length>();
[MethodImpl(MethodImplOptions.AggressiveInlining)] public static List<Length?> Sqrt(this List<Area?> units) => units.Sqrt3<Area, Length>(); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static List<Length?> Sqrt(this List<Area?> units) => units.Cbrt<Area, Length>();
// === IEnumerable<T> === // === IEnumerable<T> ===
[MethodImpl(MethodImplOptions.AggressiveInlining)] public static IEnumerable<Length> Sqrt(this IEnumerable<Area> units) => units.Sqrt3<Area, Length>(); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static IEnumerable<Length> Sqrt(this IEnumerable<Area> units) => units.Cbrt<Area, Length>();
[MethodImpl(MethodImplOptions.AggressiveInlining)] public static IEnumerable<Length?> Sqrt(this IEnumerable<Area?> units) => units.Sqrt3<Area, Length>(); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static IEnumerable<Length?> Sqrt(this IEnumerable<Area?> units) => units.Cbrt<Area, Length>();
} }
internal readonly struct AreaConv internal readonly struct AreaConv

View File

@@ -38,22 +38,22 @@ public readonly partial record struct Volume
public static class VolumeSqrtExtension public static class VolumeSqrtExtension
{ {
// === ReadOnlySpan // === ReadOnlySpan
[MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Sqrt3( [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Cbrt(
this ReadOnlySpan<Volume> units, Span<Length> destination) => units.Sqrt3<Volume, Length>(destination); this ReadOnlySpan<Volume> units, Span<Length> destination) => units.Cbrt<Volume, Length>(destination);
[MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Sqrt3( [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Cbrt(
this ReadOnlySpan<Volume?> units, Span<Length?> destination) => units.Sqrt3<Volume, Length>(destination); this ReadOnlySpan<Volume?> units, Span<Length?> destination) => units.Cbrt<Volume, Length>(destination);
// === Array === // === Array ===
[MethodImpl(MethodImplOptions.AggressiveInlining)] public static Length[] Sqrt3(this Volume[] units) => units.Sqrt3<Volume, Length>(); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Length[] Cbrt(this Volume[] units) => units.Cbrt<Volume, Length>();
[MethodImpl(MethodImplOptions.AggressiveInlining)] public static Length?[] Sqrt3(this Volume?[] units) => units.Sqrt3<Volume, Length>(); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Length?[] Cbrt(this Volume?[] units) => units.Cbrt<Volume, Length>();
// === List<T> === // === List<T> ===
[MethodImpl(MethodImplOptions.AggressiveInlining)] public static List<Length> Sqrt3(this List<Volume> units) => units.Sqrt3<Volume, Length>(); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static List<Length> Cbrt(this List<Volume> units) => units.Cbrt<Volume, Length>();
[MethodImpl(MethodImplOptions.AggressiveInlining)] public static List<Length?> Sqrt3(this List<Volume?> units) => units.Sqrt3<Volume, Length>(); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static List<Length?> Cbrt(this List<Volume?> units) => units.Cbrt<Volume, Length>();
// === IEnumerable<T> === // === IEnumerable<T> ===
[MethodImpl(MethodImplOptions.AggressiveInlining)] public static IEnumerable<Length> Sqrt3(this IEnumerable<Volume> units) => units.Sqrt3<Volume, Length>(); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static IEnumerable<Length> Cbrt(this IEnumerable<Volume> units) => units.Cbrt<Volume, Length>();
[MethodImpl(MethodImplOptions.AggressiveInlining)] public static IEnumerable<Length?> Sqrt3(this IEnumerable<Volume?> units) => units.Sqrt3<Volume, Length>(); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static IEnumerable<Length?> Cbrt(this IEnumerable<Volume?> units) => units.Cbrt<Volume, Length>();
} }
internal readonly struct VolumeConv internal readonly struct VolumeConv

File diff suppressed because it is too large Load Diff

View File

@@ -1,70 +1,70 @@
//#if DEBUG #if DEBUG
//namespace QWERTYkez.Mensura.Units; namespace QWERTYkez.Mensura.Units;
///// <summary> /// <summary>
///// Base value is MilliMeters /// Base value is MilliMeters
///// </summary> /// </summary>
//[DebuggerDisplay("mm = {_MilliMeters.ToString(\"0.###\")}, m = {Meters.ToString(\"0.###\")}")] [DebuggerDisplay("mm = {_MilliMeters.ToString(\"0.###\")}, m = {Meters.ToString(\"0.###\")}")]
//public readonly partial record struct XXXXXXXX public readonly partial record struct XXXXXXXX
//{ {
// public static XXXXXXXX MilliMeter { get; } = new(1); public static XXXXXXXX MilliMeter { get; } = new(1);
// [NotMapped, JsonIgnore, IgnoreDataMember] public double _MilliMeters { get => _Value; init => _Value = value; } [NotMapped, JsonIgnore, IgnoreDataMember] public double _MilliMeters { get => _Value; init => _Value = value; }
// public static XXXXXXXX CentiMeter { get; } = new(XXXXXXXXConv.CentiMeters.To(1)); public static XXXXXXXX CentiMeter { get; } = new(XXXXXXXXConv.CentiMeters.To(1));
// [NotMapped, JsonIgnore, IgnoreDataMember] [NotMapped, JsonIgnore, IgnoreDataMember]
// public double CentiMeters public double CentiMeters
// { {
// get => XXXXXXXXConv.CentiMeters.From(_Value); get => XXXXXXXXConv.CentiMeters.From(_Value);
// init init
// { {
// _Value = XXXXXXXXConv.CentiMeters.To(value); _Value = XXXXXXXXConv.CentiMeters.To(value);
// } }
// } }
// public static XXXXXXXX DeciMeter { get; } = new(XXXXXXXXConv.DeciMeters.To(1)); public static XXXXXXXX DeciMeter { get; } = new(XXXXXXXXConv.DeciMeters.To(1));
// [NotMapped, JsonIgnore, IgnoreDataMember] [NotMapped, JsonIgnore, IgnoreDataMember]
// public double DeciMeters public double DeciMeters
// { {
// get => XXXXXXXXConv.DeciMeters.From(_Value); get => XXXXXXXXConv.DeciMeters.From(_Value);
// init => _Value = XXXXXXXXConv.DeciMeters.To(value); init => _Value = XXXXXXXXConv.DeciMeters.To(value);
// } }
// public static XXXXXXXX Meter { get; } = new(XXXXXXXXConv.Meters.To(1)); public static XXXXXXXX Meter { get; } = new(XXXXXXXXConv.Meters.To(1));
// [NotMapped, JsonIgnore, IgnoreDataMember] [NotMapped, JsonIgnore, IgnoreDataMember]
// public double Meters public double Meters
// { {
// get => XXXXXXXXConv.Meters.From(_Value); get => XXXXXXXXConv.Meters.From(_Value);
// init => _Value = XXXXXXXXConv.Meters.To(value); init => _Value = XXXXXXXXConv.Meters.To(value);
// } }
// public static XXXXXXXX KiloMeter { get; } = new(XXXXXXXXConv.KiloMeters.To(1)); public static XXXXXXXX KiloMeter { get; } = new(XXXXXXXXConv.KiloMeters.To(1));
// [NotMapped, JsonIgnore, IgnoreDataMember] [NotMapped, JsonIgnore, IgnoreDataMember]
// public double KiloMeters public double KiloMeters
// { {
// get => XXXXXXXXConv.KiloMeters.From(_Value); get => XXXXXXXXConv.KiloMeters.From(_Value);
// init => _Value = XXXXXXXXConv.KiloMeters.To(value); init => _Value = XXXXXXXXConv.KiloMeters.To(value);
// } }
// public XXXXXXXX AddMilliMeters(double value) => new(_Value + value); public XXXXXXXX AddMilliMeters(double value) => new(_Value + value);
// public XXXXXXXX AddCentiMeters(double value) => new(_Value + XXXXXXXXConv.CentiMeters.To(value)); public XXXXXXXX AddCentiMeters(double value) => new(_Value + XXXXXXXXConv.CentiMeters.To(value));
// public XXXXXXXX AddDeciMeters(double value) => new(_Value + XXXXXXXXConv.DeciMeters.To(value)); public XXXXXXXX AddDeciMeters(double value) => new(_Value + XXXXXXXXConv.DeciMeters.To(value));
// public XXXXXXXX AddMeters(double value) => new(_Value + XXXXXXXXConv.Meters.To(value)); public XXXXXXXX AddMeters(double value) => new(_Value + XXXXXXXXConv.Meters.To(value));
// public XXXXXXXX AddKiloMeters(double value) => new(_Value + XXXXXXXXConv.KiloMeters.To(value)); public XXXXXXXX AddKiloMeters(double value) => new(_Value + XXXXXXXXConv.KiloMeters.To(value));
//} }
//internal readonly struct XXXXXXXXConv internal readonly struct XXXXXXXXConv
//{ {
// private XXXXXXXXConv(double multiplicator) => this.Multiplicator = multiplicator; private XXXXXXXXConv(double multiplicator) => this.Multiplicator = multiplicator;
// public double To(double value) => value * Multiplicator; public double To(double value) => value * Multiplicator;
// public double From(double value) => value / Multiplicator; public double From(double value) => value / Multiplicator;
// public double Multiplicator { get; init; } public double Multiplicator { get; init; }
// public static XXXXXXXXConv MilliMeters { get; } = new(1); public static XXXXXXXXConv MilliMeters { get; } = new(1);
// public static XXXXXXXXConv CentiMeters { get; } = new(10); public static XXXXXXXXConv CentiMeters { get; } = new(10);
// public static XXXXXXXXConv DeciMeters { get; } = new(100); public static XXXXXXXXConv DeciMeters { get; } = new(100);
// public static XXXXXXXXConv Meters { get; } = new(1000); public static XXXXXXXXConv Meters { get; } = new(1000);
// public static XXXXXXXXConv KiloMeters { get; } = new(1000000); public static XXXXXXXXConv KiloMeters { get; } = new(1000000);
//} }
//#endif #endif