Files
QWERTYkez.Mensura/QWERTYkez.Mensura.Generator/UnitOperatorsGenerator.cs
melekhin ba77411c4a pogon...
2026-05-29 16:45:24 +07:00

1794 lines
75 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Text;
using System.Text;
namespace QWERTYkez.Mensura.Generator;
[Generator]
public class UnitOperatorsGenerator : IIncrementalGenerator
{
private const string AttributeName = "UnitOperatorsGenerator";
public void Initialize(IncrementalGeneratorInitializationContext context)
{
// Генерируем атрибут
context.RegisterPostInitializationOutput(ctx =>
{
string attributeSource = @"
namespace QWERTYkez.Mensura
{
[System.AttributeUsage(System.AttributeTargets.Struct, AllowMultiple = false)]
public sealed class UnitOperatorsGeneratorAttribute : System.Attribute { }
}";
ctx.AddSource("UnitOperatorsGeneratorAttribute.g.cs", SourceText.From(attributeSource, Encoding.UTF8));
});
// Ищем все readonly partial record struct с атрибутом
var structsProvider = context.SyntaxProvider
.CreateSyntaxProvider(
predicate: static (node, _) => IsTargetStruct(node),
transform: static (ctx, _) => GetStructInfo(ctx))
.Where(info => info.HasValue)
.Select((info, _) => info!.Value)
.Collect();
context.RegisterSourceOutput(structsProvider, (spc, structs) =>
{
foreach (var structInfo in structs)
{
string generatedCode = GeneratePartial(structInfo);
spc.AddSource($"{structInfo.TypeName}.Generated.g.cs", SourceText.From(generatedCode, Encoding.UTF8));
}
});
}
private static bool IsTargetStruct(SyntaxNode node)
{
if (node is not RecordDeclarationSyntax record)
return false;
// Должен быть record struct с модификаторами readonly и partial
if (!record.Modifiers.Any(SyntaxKind.ReadOnlyKeyword) ||
!record.Modifiers.Any(SyntaxKind.PartialKeyword) ||
!record.Keyword.IsKind(SyntaxKind.RecordKeyword) ||
!record.ClassOrStructKeyword.IsKind(SyntaxKind.StructKeyword))
return false;
// Проверяем наличие атрибута [UnitOperatorsGenerator]
foreach (var attrList in record.AttributeLists)
foreach (var attr in attrList.Attributes)
{
string name = attr.Name.ToString();
if (name == AttributeName || name == AttributeName + "Attribute")
return true;
}
return false;
}
private static StructInfo? GetStructInfo(GeneratorSyntaxContext context)
{
var record = (RecordDeclarationSyntax)context.Node;
var semanticModel = context.SemanticModel;
if (semanticModel.GetDeclaredSymbol(record) is not INamedTypeSymbol typeSymbol)
return null;
string namespaceName = typeSymbol.ContainingNamespace?.ToString();
if (string.IsNullOrEmpty(namespaceName))
return null;
return new StructInfo(namespaceName, typeSymbol.Name);
}
private static string GeneratePartial(StructInfo info)
{
string typeName = info.TypeName;
string ns = info.Namespace;
// Здесь должен быть полный код из вашего файла XXXXXXXXXXXX.cs
// с заменой XXXXXXXXXXXXXX на {typeName}. Для краткости приведён скелет.
// Вы должны скопировать сюда всё содержимое вашего второго файла,
// заменив XXXXXXXXXXXXXX на {typeName}.
string skeleton = @"
global using {typeName} = QWERTYkez.Mensura.Units.{typeName};
global using {typeName}Extensions = QWERTYkez.Mensura.Units.{typeName}Extensions;
global using {typeName}Converter = QWERTYkez.Mensura.Units.{typeName}Converter;
using System.Globalization;
using System.Runtime.Serialization;
namespace QWERTYkez.Mensura.Units;
[JsonConverter(typeof({typeName}Converter))]
public readonly partial record struct {typeName} : IMensuraUnit<{typeName}>, IEquatable<{typeName}>, IMensuraUnit
{
[JsonInclude, DataMember, JsonPropertyName(""v"")] // для JSON / EF на случай сбоев, если пробелма с _Value
internal double Value { get => _Value; init => _Value = value; }
double IMensuraUnit.Value { get => _Value; init => _Value = value; }
internal readonly double _Value;
internal {typeName}(double value) => _Value = value;
public override int GetHashCode() => _Value.GetHashCode();
public int CompareTo({typeName}? other) => _Value.CompareTo(other is null ? 0d : other.Value._Value);
public int CompareTo({typeName} other) => _Value.CompareTo(other._Value);
public bool Equals({typeName}? other) => _Value.Equals(other?._Value);
[JsonIgnore, IgnoreDataMember] public bool IsPositive => _Value >= 0;
[JsonIgnore, IgnoreDataMember] public bool IsGreaterThanZero => _Value > 0;
[JsonIgnore, IgnoreDataMember] public bool IsNegative => double.IsNegative(_Value);
[JsonIgnore, IgnoreDataMember] public bool IsZero => _Value == 0;
[JsonIgnore, IgnoreDataMember] public bool IsNaN => double.IsNaN(_Value);
[JsonIgnore, IgnoreDataMember] public bool IsFinite => double.IsFinite(_Value);
[JsonIgnore, IgnoreDataMember] public bool IsInfinity => double.IsInfinity(_Value);
[JsonIgnore, IgnoreDataMember] public bool IsPositiveInfinity => double.IsPositiveInfinity(_Value);
[JsonIgnore, IgnoreDataMember] public bool IsNegativeInfinity => double.IsNegativeInfinity(_Value);
public static {typeName} Zero { get; } = new(0d);
public static {typeName} Min { get; } = new(double.MinValue);
public static {typeName} Max { get; } = new(double.MaxValue);
public static {typeName} NegativeInfinity { get; } = new(double.NegativeInfinity);
public static {typeName} PositiveInfinity { get; } = new(double.PositiveInfinity);
public static bool operator ==({typeName}? T1, {typeName}? T2) => (T1 is null ? 0d : T1.Value._Value) == (T2 is null ? 0d : T2.Value._Value);
public static bool operator !=({typeName}? T1, {typeName}? T2) => (T1 is null ? 0d : T1.Value._Value) != (T2 is null ? 0d : T2.Value._Value);
public static bool operator <({typeName}? T1, {typeName}? T2) => (T1 is null ? 0d : T1.Value._Value) < (T2 is null ? 0d : T2.Value._Value);
public static bool operator <=({typeName}? T1, {typeName}? T2) => (T1 is null ? 0d : T1.Value._Value) <= (T2 is null ? 0d : T2.Value._Value);
public static bool operator >({typeName}? T1, {typeName}? T2) => (T1 is null ? 0d : T1.Value._Value) > (T2 is null ? 0d : T2.Value._Value);
public static bool operator >=({typeName}? T1, {typeName}? T2) => (T1 is null ? 0d : T1.Value._Value) >= (T2 is null ? 0d : T2.Value._Value);
public static {typeName} operator +({typeName} T2) => new(+T2._Value);
public static {typeName} operator +({typeName} T1, {typeName} T2) => new(T1._Value + T2._Value);
public static {typeName} operator -({typeName} T2) => new(-T2._Value);
public static {typeName} operator -({typeName} T1, {typeName} T2) => new(T1._Value - T2._Value);
// double
public static {typeName} operator *({typeName} T1, double T2) => new(T1._Value * T2);
public static {typeName} operator *({typeName} T1, double? T2) => T1 * (T2 ?? 0d);
public static {typeName} operator *(double T1, {typeName} T2) => new(T1 * T2._Value);
public static {typeName} operator *(double? T1, {typeName} T2) => (T1 ?? 0d) * T2;
public static {typeName} operator /({typeName} T1, double T2) => new(T1._Value / T2);
public static {typeName} operator /({typeName} T1, double? T2) => T1 / (T2 ?? 0d);
public static double operator /({typeName} T1, {typeName} T2) => T1._Value / T2._Value;
// sbyte
public static {typeName} operator *({typeName} T1, sbyte T2) => T1 * T2.ToDouble();
public static {typeName} operator *({typeName} T1, sbyte? T2) => T1 * T2.ToDouble();
public static {typeName} operator *(sbyte T1, {typeName} T2) => T1.ToDouble() * T2;
public static {typeName} operator *(sbyte? T1, {typeName} T2) => T1.ToDouble() * T2;
public static {typeName} operator /({typeName} T1, sbyte T2) => T1 / T2.ToDouble();
public static {typeName} operator /({typeName} T1, sbyte? T2) => T1 / T2.ToDouble();
// short
public static {typeName} operator *({typeName} T1, short T2) => T1 * T2.ToDouble();
public static {typeName} operator *({typeName} T1, short? T2) => T1 * T2.ToDouble();
public static {typeName} operator *(short T1, {typeName} T2) => T1.ToDouble() * T2;
public static {typeName} operator *(short? T1, {typeName} T2) => T1.ToDouble() * T2;
public static {typeName} operator /({typeName} T1, short T2) => T1 / T2.ToDouble();
public static {typeName} operator /({typeName} T1, short? T2) => T1 / T2.ToDouble();
// int
public static {typeName} operator *({typeName} T1, int T2) => T1 * T2.ToDouble();
public static {typeName} operator *({typeName} T1, int? T2) => T1 * T2.ToDouble();
public static {typeName} operator *(int T1, {typeName} T2) => T1.ToDouble() * T2;
public static {typeName} operator *(int? T1, {typeName} T2) => T1.ToDouble() * T2;
public static {typeName} operator /({typeName} T1, int T2) => T1 / T2.ToDouble();
public static {typeName} operator /({typeName} T1, int? T2) => T1 / T2.ToDouble();
// long
public static {typeName} operator *({typeName} T1, long T2) => T1 * T2.ToDouble();
public static {typeName} operator *({typeName} T1, long? T2) => T1 * T2.ToDouble();
public static {typeName} operator *(long T1, {typeName} T2) => T1.ToDouble() * T2;
public static {typeName} operator *(long? T1, {typeName} T2) => T1.ToDouble() * T2;
public static {typeName} operator /({typeName} T1, long T2) => T1 / T2.ToDouble();
public static {typeName} operator /({typeName} T1, long? T2) => T1 / T2.ToDouble();
// byte
public static {typeName} operator *({typeName} T1, byte T2) => T1 * T2.ToDouble();
public static {typeName} operator *({typeName} T1, byte? T2) => T1 * T2.ToDouble();
public static {typeName} operator *(byte T1, {typeName} T2) => T1.ToDouble() * T2;
public static {typeName} operator *(byte? T1, {typeName} T2) => T1.ToDouble() * T2;
public static {typeName} operator /({typeName} T1, byte T2) => T1 / T2.ToDouble();
public static {typeName} operator /({typeName} T1, byte? T2) => T1 / T2.ToDouble();
// ushort
public static {typeName} operator *({typeName} T1, ushort T2) => T1 * T2.ToDouble();
public static {typeName} operator *({typeName} T1, ushort? T2) => T1 * T2.ToDouble();
public static {typeName} operator *(ushort T1, {typeName} T2) => T1.ToDouble() * T2;
public static {typeName} operator *(ushort? T1, {typeName} T2) => T1.ToDouble() * T2;
public static {typeName} operator /({typeName} T1, ushort T2) => T1 / T2.ToDouble();
public static {typeName} operator /({typeName} T1, ushort? T2) => T1 / T2.ToDouble();
// uint
public static {typeName} operator *({typeName} T1, uint T2) => T1 * T2.ToDouble();
public static {typeName} operator *({typeName} T1, uint? T2) => T1 * T2.ToDouble();
public static {typeName} operator *(uint T1, {typeName} T2) => T1.ToDouble() * T2;
public static {typeName} operator *(uint? T1, {typeName} T2) => T1.ToDouble() * T2;
public static {typeName} operator /({typeName} T1, uint T2) => T1 / T2.ToDouble();
public static {typeName} operator /({typeName} T1, uint? T2) => T1 / T2.ToDouble();
// ulong
public static {typeName} operator *({typeName} T1, ulong T2) => T1 * T2.ToDouble();
public static {typeName} operator *({typeName} T1, ulong? T2) => T1 * T2.ToDouble();
public static {typeName} operator *(ulong T1, {typeName} T2) => T1.ToDouble() * T2;
public static {typeName} operator *(ulong? T1, {typeName} T2) => T1.ToDouble() * T2;
public static {typeName} operator /({typeName} T1, ulong T2) => T1 / T2.ToDouble();
public static {typeName} operator /({typeName} T1, ulong? T2) => T1 / T2.ToDouble();
// nint
public static {typeName} operator *({typeName} T1, nint T2) => T1 * T2.ToDouble();
public static {typeName} operator *({typeName} T1, nint? T2) => T1 * T2.ToDouble();
public static {typeName} operator *(nint T1, {typeName} T2) => T1.ToDouble() * T2;
public static {typeName} operator *(nint? T1, {typeName} T2) => T1.ToDouble() * T2;
public static {typeName} operator /({typeName} T1, nint T2) => T1 / T2.ToDouble();
public static {typeName} operator /({typeName} T1, nint? T2) => T1 / T2.ToDouble();
// nuint
public static {typeName} operator *({typeName} T1, nuint T2) => T1 * T2.ToDouble();
public static {typeName} operator *({typeName} T1, nuint? T2) => T1 * T2.ToDouble();
public static {typeName} operator *(nuint T1, {typeName} T2) => T1.ToDouble() * T2;
public static {typeName} operator *(nuint? T1, {typeName} T2) => T1.ToDouble() * T2;
public static {typeName} operator /({typeName} T1, nuint T2) => T1 / T2.ToDouble();
public static {typeName} operator /({typeName} T1, nuint? T2) => T1 / T2.ToDouble();
// float
public static {typeName} operator *({typeName} T1, float T2) => T1 * T2.ToDouble();
public static {typeName} operator *({typeName} T1, float? T2) => T1 * T2.ToDouble();
public static {typeName} operator *(float T1, {typeName} T2) => T1.ToDouble() * T2;
public static {typeName} operator *(float? T1, {typeName} T2) => T1.ToDouble() * T2;
public static {typeName} operator /({typeName} T1, float T2) => T1 / T2.ToDouble();
public static {typeName} operator /({typeName} T1, float? T2) => T1 / T2.ToDouble();
// decimal
public static {typeName} operator *({typeName} T1, decimal T2) => T1 * T2.ToDouble();
public static {typeName} operator *({typeName} T1, decimal? T2) => T1 * T2.ToDouble();
public static {typeName} operator *(decimal T1, {typeName} T2) => T1.ToDouble() * T2;
public static {typeName} operator *(decimal? T1, {typeName} T2) => T1.ToDouble() * T2;
public static {typeName} operator /({typeName} T1, decimal T2) => T1 / T2.ToDouble();
public static {typeName} operator /({typeName} T1, decimal? T2) => T1 / T2.ToDouble();
#if NET7_0_OR_GREATER
// Int128
public static {typeName} operator *({typeName} T1, Int128 T2) => T1 * T2.ToDouble();
public static {typeName} operator *({typeName} T1, Int128? T2) => T1 * T2.ToDouble();
public static {typeName} operator *(Int128 T1, {typeName} T2) => T1.ToDouble() * T2;
public static {typeName} operator *(Int128? T1, {typeName} T2) => T1.ToDouble() * T2;
public static {typeName} operator /({typeName} T1, Int128 T2) => T1 / T2.ToDouble();
public static {typeName} operator /({typeName} T1, Int128? T2) => T1 / T2.ToDouble();
// UInt128
public static {typeName} operator *({typeName} T1, UInt128 T2) => T1 * T2.ToDouble();
public static {typeName} operator *({typeName} T1, UInt128? T2) => T1 * T2.ToDouble();
public static {typeName} operator *(UInt128 T1, {typeName} T2) => T1.ToDouble() * T2;
public static {typeName} operator *(UInt128? T1, {typeName} T2) => T1.ToDouble() * T2;
public static {typeName} operator /({typeName} T1, UInt128 T2) => T1 / T2.ToDouble();
public static {typeName} operator /({typeName} T1, UInt128? T2) => T1 / T2.ToDouble();
#endif
public static explicit operator {typeName}(double val) => new(val);
public static explicit operator double({typeName} unit) => unit._Value;
public {typeName} Abs() => new(Math.Abs(_Value));
/// <summary>C^2 = this^2 + B^2</summary> <returns>C = (this^2 + B^2).Sqrt(2)</returns>
public {typeName} HypFromLeg({typeName} B) => new(Math.Sqrt(_Value * _Value + B._Value * B._Value));
/// <summary>C^2 = this^2 + B^2</summary> <returns>C = (this^2 + B^2).Sqrt(2)</returns>
public {typeName} HypFromLeg({typeName}? B)
{
double b = B is null ? 0d : B.Value._Value;
return new(Math.Sqrt(_Value * _Value + b * b));
}
/// <summary>C^2 = this^2 + B^2</summary> <returns>B = (C^2 - this^2).Sqrt(2)</returns>
public {typeName} LegFromHyp({typeName} C) => new(Math.Sqrt(C._Value * C._Value - _Value * _Value));
/// <summary>C^2 = this^2 + B^2</summary> <returns>B = (C^2 - this^2).Sqrt(2)</returns>
public {typeName} LegFromHyp({typeName}? C)
{
double c = C is null ? 0d : C.Value._Value;
return new(Math.Sqrt(c * c - _Value * _Value));
}
/// <summary>this^2 = A^2 + B^2</summary> <returns>B = (this^2 - A^2).Sqrt(2)</returns>
public {typeName} LegFromLeg({typeName} A) => new(Math.Sqrt(_Value * _Value - A._Value * A._Value));
/// <summary>this^2 = A^2 + B^2</summary> <returns>B = (this^2 - A^2).Sqrt(2)</returns>
public {typeName} LegFromLeg({typeName}? A)
{
double a = A is null ? 0d : A.Value._Value;
return new(Math.Sqrt(_Value * _Value - a * a));
}
}
public static class {typeName}Extensions
{
internal static double ToDouble(this {typeName}? unit) => unit is null ? 0d : unit.Value._Value;
public static {typeName} MetricSum(this IEnumerable<{typeName}> units) => new() { Value = units?.Sum(m => m._Value) ?? 0d };
public static {typeName} MetricAverage(this IEnumerable<{typeName}> units) => new() { Value = units?.Average(m => m._Value) ?? double.NaN };
public static {typeName} MetricMax(this IEnumerable<{typeName}> units) => new() { Value = units?.Max(m => m._Value) ?? double.MinValue };
public static {typeName} MetricMin(this IEnumerable<{typeName}> units) => new() { Value = units?.Min(m => m._Value) ?? double.MaxValue };
public static {typeName} MetricSum(this IEnumerable<{typeName}?> units) => new() { Value = units?.Sum(m => m.ToDouble()) ?? 0d };
public static {typeName} MetricAverage(this IEnumerable<{typeName}?> units) => new() { Value = units?.Average(m => m.ToDouble()) ?? double.NaN };
public static {typeName} MetricMax(this IEnumerable<{typeName}?> units) => new() { Value = units?.Max(m => m.ToDouble()) ?? double.MinValue };
public static {typeName} MetricMin(this IEnumerable<{typeName}?> units) => new() { Value = units?.Min(m => m.ToDouble()) ?? double.MaxValue };
internal static void MultiplyCore(ReadOnlySpan<{typeName}> source, double value, Span<{typeName}> destination)
{
int len = source.Length;
ReadOnlySpan<double> srcDouble = MemoryMarshal.Cast<{typeName}, double>(source);
Span<double> dstDouble = MemoryMarshal.Cast<{typeName}, double>(destination);
var vectorizedValue = new Vector<double>(value);
int vectorSize = Vector<double>.Count;
int i = 0;
ref double srcRef = ref MemoryMarshal.GetReference(srcDouble);
ref double dstRef = ref MemoryMarshal.GetReference(dstDouble);
int SIMDEnd = len - (len % vectorSize);
for (; i < SIMDEnd; i += vectorSize)
{
ref double currentSrc = ref Unsafe.Add(ref srcRef, i);
ReadOnlySpan<double> srcWindow = MemoryMarshal.CreateReadOnlySpan(ref currentSrc, vectorSize);
var vector = new Vector<double>(srcWindow);
var multiplied = vector * vectorizedValue;
ref double currentDst = ref Unsafe.Add(ref dstRef, i);
Span<double> dstWindow = MemoryMarshal.CreateSpan(ref currentDst, vectorSize);
multiplied.CopyTo(dstWindow);
}
for (; i < len; i++)
{
Unsafe.Add(ref dstRef, i) = Unsafe.Add(ref srcRef, i) * value;
}
}
internal static void DivideCore(ReadOnlySpan<{typeName}> source, double divisor, Span<{typeName}> destination)
{
// 1. Проверка на ноль
if (divisor == 0d || double.IsNaN(divisor))
throw new DivideByZeroException(""Делитель не может быть равен нулю."");
int len = source.Length;
ReadOnlySpan<double> srcDouble = MemoryMarshal.Cast<{typeName}, double>(source);
Span<double> dstDouble = MemoryMarshal.Cast<{typeName}, double>(destination);
var vectorizedValue = new Vector<double>(divisor);
int vectorSize = Vector<double>.Count;
int i = 0;
ref double srcRef = ref MemoryMarshal.GetReference(srcDouble);
ref double dstRef = ref MemoryMarshal.GetReference(dstDouble);
int SIMDEnd = len - (len % vectorSize);
for (; i < SIMDEnd; i += vectorSize)
{
ref double currentSrc = ref Unsafe.Add(ref srcRef, i);
ReadOnlySpan<double> srcWindow = MemoryMarshal.CreateReadOnlySpan(ref currentSrc, vectorSize);
var vector = new Vector<double>(srcWindow);
var multiplied = vector / vectorizedValue;
ref double currentDst = ref Unsafe.Add(ref dstRef, i);
Span<double> dstWindow = MemoryMarshal.CreateSpan(ref currentDst, vectorSize);
multiplied.CopyTo(dstWindow);
}
for (; i < len; i++)
{
Unsafe.Add(ref dstRef, i) = Unsafe.Add(ref srcRef, i) / divisor;
}
}
internal static void DivideCore(double dividend, ReadOnlySpan<{typeName}> source, Span<{typeName}> destination)
{
int len = source.Length;
ReadOnlySpan<double> srcDouble = MemoryMarshal.Cast<{typeName}, double>(source);
Span<double> dstDouble = MemoryMarshal.Cast<{typeName}, double>(destination);
var vectorizedValue = new Vector<double>(dividend);
var zeroVector = Vector<double>.Zero; // Вектор из нулей для сравнения
int vectorSize = Vector<double>.Count;
int i = 0;
ref double srcRef = ref MemoryMarshal.GetReference(srcDouble);
ref double dstRef = ref MemoryMarshal.GetReference(dstDouble);
int SIMDEnd = len - (len % vectorSize);
for (; i < SIMDEnd; i += vectorSize)
{
ref double currentSrc = ref Unsafe.Add(ref srcRef, i);
ReadOnlySpan<double> srcWindow = MemoryMarshal.CreateReadOnlySpan(ref currentSrc, vectorSize);
var vector = new Vector<double>(srcWindow);
// БЫСТРАЯ ПРОВЕРКА: Есть ли хотя бы один 0.0 в текущем векторе?
if (Vector.EqualsAny(vector, zeroVector))
{
throw new DivideByZeroException($""Обнаружен делитель, равный нулю, в районе индексов {i}..{i + vectorSize - 1}."");
}
var multiplied = vectorizedValue / vector;
ref double currentDst = ref Unsafe.Add(ref dstRef, i);
Span<double> dstWindow = MemoryMarshal.CreateSpan(ref currentDst, vectorSize);
multiplied.CopyTo(dstWindow);
}
// Хвостовой цикл
for (; i < len; i++)
{
double divisor = Unsafe.Add(ref srcRef, i);
if (divisor == 0.0)
{
throw new DivideByZeroException($""Обнаружен делитель, равный нулю, в индексе {i}."");
}
Unsafe.Add(ref dstRef, i) = dividend / divisor;
}
}
internal static void PlusCore(ReadOnlySpan<{typeName}> source, double summand, Span<{typeName}> destination)
{
int len = source.Length;
ReadOnlySpan<double> srcDouble = MemoryMarshal.Cast<{typeName}, double>(source);
Span<double> dstDouble = MemoryMarshal.Cast<{typeName}, double>(destination);
var vectorizedValue = new Vector<double>(summand);
int vectorSize = Vector<double>.Count;
int i = 0;
ref double srcRef = ref MemoryMarshal.GetReference(srcDouble);
ref double dstRef = ref MemoryMarshal.GetReference(dstDouble);
int SIMDEnd = len - (len % vectorSize);
for (; i < SIMDEnd; i += vectorSize)
{
ref double currentSrc = ref Unsafe.Add(ref srcRef, i);
ReadOnlySpan<double> srcWindow = MemoryMarshal.CreateReadOnlySpan(ref currentSrc, vectorSize);
var vector = new Vector<double>(srcWindow);
var multiplied = vector + vectorizedValue;
ref double currentDst = ref Unsafe.Add(ref dstRef, i);
Span<double> dstWindow = MemoryMarshal.CreateSpan(ref currentDst, vectorSize);
multiplied.CopyTo(dstWindow);
}
for (; i < len; i++)
{
Unsafe.Add(ref dstRef, i) = Unsafe.Add(ref srcRef, i) + summand;
}
}
internal static void MinusCore(ReadOnlySpan<{typeName}> source, double subtrahend, Span<{typeName}> destination)
{
int len = source.Length;
ReadOnlySpan<double> srcDouble = MemoryMarshal.Cast<{typeName}, double>(source);
Span<double> dstDouble = MemoryMarshal.Cast<{typeName}, double>(destination);
var vectorizedValue = new Vector<double>(subtrahend);
int vectorSize = Vector<double>.Count;
int i = 0;
ref double srcRef = ref MemoryMarshal.GetReference(srcDouble);
ref double dstRef = ref MemoryMarshal.GetReference(dstDouble);
int SIMDEnd = len - (len % vectorSize);
for (; i < SIMDEnd; i += vectorSize)
{
ref double currentSrc = ref Unsafe.Add(ref srcRef, i);
ReadOnlySpan<double> srcWindow = MemoryMarshal.CreateReadOnlySpan(ref currentSrc, vectorSize);
var vector = new Vector<double>(srcWindow);
var multiplied = vector - vectorizedValue;
ref double currentDst = ref Unsafe.Add(ref dstRef, i);
Span<double> dstWindow = MemoryMarshal.CreateSpan(ref currentDst, vectorSize);
multiplied.CopyTo(dstWindow);
}
for (; i < len; i++)
{
Unsafe.Add(ref dstRef, i) = Unsafe.Add(ref srcRef, i) - subtrahend;
}
}
internal static void MinusCore(double minuend, ReadOnlySpan<{typeName}> source, Span<{typeName}> destination)
{
int len = source.Length;
ReadOnlySpan<double> srcDouble = MemoryMarshal.Cast<{typeName}, double>(source);
Span<double> dstDouble = MemoryMarshal.Cast<{typeName}, double>(destination);
var vectorizedValue = new Vector<double>(minuend);
int vectorSize = Vector<double>.Count;
int i = 0;
ref double srcRef = ref MemoryMarshal.GetReference(srcDouble);
ref double dstRef = ref MemoryMarshal.GetReference(dstDouble);
int SIMDEnd = len - (len % vectorSize);
for (; i < SIMDEnd; i += vectorSize)
{
ref double currentSrc = ref Unsafe.Add(ref srcRef, i);
ReadOnlySpan<double> srcWindow = MemoryMarshal.CreateReadOnlySpan(ref currentSrc, vectorSize);
var vector = new Vector<double>(srcWindow);
var multiplied = vectorizedValue - vector;
ref double currentDst = ref Unsafe.Add(ref dstRef, i);
Span<double> dstWindow = MemoryMarshal.CreateSpan(ref currentDst, vectorSize);
multiplied.CopyTo(dstWindow);
}
for (; i < len; i++)
{
Unsafe.Add(ref dstRef, i) = minuend - Unsafe.Add(ref srcRef, i);
}
}
[MethodImpl(MethodImplOptions.AggressiveOptimization)]
internal static void PowCore(ReadOnlySpan<{typeName}> source, int power, Span<{typeName}> destination)
{
int len = source.Length;
ReadOnlySpan<double> srcDouble = MemoryMarshal.Cast<{typeName}, double>(source);
Span<double> dstDouble = MemoryMarshal.Cast<{typeName}, double>(destination);
int vectorSize = Vector<double>.Count;
int i = 0;
ref double srcRef = ref MemoryMarshal.GetReference(srcDouble);
ref double dstRef = ref MemoryMarshal.GetReference(dstDouble);
int SIMDEnd = len - (len % vectorSize);
for (; i < SIMDEnd; i += vectorSize)
{
var vector = new Vector<double>(MemoryMarshal.CreateReadOnlySpan(ref Unsafe.Add(ref srcRef, i), vectorSize));
var resultVector = VectorPow(vector, power);
resultVector.CopyTo(MemoryMarshal.CreateSpan(ref Unsafe.Add(ref dstRef, i), vectorSize));
}
// Приватный SIMD-метод быстрого возведения в степень
[MethodImpl(MethodImplOptions.AggressiveInlining)]
static Vector<double> VectorPow(Vector<double> baseVector, int exp)
{
if (exp == 0) return Vector<double>.One;
if (exp == 1) return baseVector;
if (exp < 0)
{
baseVector = Vector<double>.One / baseVector;
exp = -exp; // Внимание: может переполниться при int.MinValue, но для степеней это редчайший кейс
}
var result = Vector<double>.One;
var currentBase = baseVector;
while (exp > 0)
{
if ((exp & 1) == 1)
{
result *= currentBase;
}
currentBase *= currentBase;
exp >>= 1;
}
return result;
}
for (; i < len; i++)
Unsafe.Add(ref dstRef, i) = Math.Pow(Unsafe.Add(ref srcRef, i), power);
}
[MethodImpl(MethodImplOptions.AggressiveOptimization)]
internal static unsafe void PowCore(ReadOnlySpan<{typeName}> source, double power, Span<{typeName}> destination)
{
int len = source.Length;
if (len == 0) return;
fixed (double* pSrc = MemoryMarshal.Cast<{typeName}, double>(source))
fixed (double* pDst = MemoryMarshal.Cast<{typeName}, double>(destination))
{
double* pCurrentSrc = pSrc;
double* pCurrentDst = pDst;
double* pEnd = pSrc + len;
while (pCurrentSrc <= pEnd - 4)
{
pCurrentDst[0] = Math.Pow(pCurrentSrc[0], power);
pCurrentDst[1] = Math.Pow(pCurrentSrc[1], power);
pCurrentDst[2] = Math.Pow(pCurrentSrc[2], power);
pCurrentDst[3] = Math.Pow(pCurrentSrc[3], power);
pCurrentSrc += 4;
pCurrentDst += 4;
}
while (pCurrentSrc < pEnd)
{
*pCurrentDst = Math.Pow(*pCurrentSrc, power);
pCurrentSrc++;
pCurrentDst++;
}
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static void SqrtCore(ReadOnlySpan<{typeName}> source, Span<{typeName}> destination)
{
int len = source.Length;
ReadOnlySpan<double> srcDouble = MemoryMarshal.Cast<{typeName}, double>(source);
Span<double> dstDouble = MemoryMarshal.Cast<{typeName}, double>(destination);
int vectorSize = Vector<double>.Count;
int i = 0;
ref double srcRef = ref MemoryMarshal.GetReference(srcDouble);
ref double dstRef = ref MemoryMarshal.GetReference(dstDouble);
int SIMDEnd = len - (len % vectorSize);
for (; i < SIMDEnd; i += vectorSize)
{
ref double currentSrc = ref Unsafe.Add(ref srcRef, i);
ReadOnlySpan<double> srcWindow = MemoryMarshal.CreateReadOnlySpan(ref currentSrc, vectorSize);
var vector = new Vector<double>(srcWindow);
var multiplied = Vector.SquareRoot(vector);
ref double currentDst = ref Unsafe.Add(ref dstRef, i);
Span<double> dstWindow = MemoryMarshal.CreateSpan(ref currentDst, vectorSize);
multiplied.CopyTo(dstWindow);
}
for (; i < len; i++)
{
Unsafe.Add(ref dstRef, i) = Math.Sqrt(Unsafe.Add(ref srcRef, i));
}
}
// ==========================================
// === MULTIPLY ===
// ==========================================
public static {typeName}[] Multiply(this {typeName}[] units, double multiplicator)
{
if (units is null) return null!;
if (units.Length == 0) return [];
var result = new {typeName}[units.Length];
MultiplyCore(units, multiplicator, result);
return result;
}
public static {typeName}[] Multiply(this double multiplicator, {typeName}[] units)
=> units.Multiply(multiplicator);
public static {typeName}?[] Multiply(this {typeName}?[] units, double multiplicator)
{
if (units is null) return null!;
if (units.Length == 0) return [];
var result = ({typeName}?[])units.Clone();
int len = result.Length;
for (int i = 0; i < len; i++)
{
ref var item = ref result[i];
if (item.HasValue) item = new {typeName}(item.Value._Value * multiplicator);
}
return result;
}
public static {typeName}?[] Multiply(this double multiplicator, {typeName}?[] units)
=> units.Multiply(multiplicator);
// === ReadOnlySpan ===
public static Span<{typeName}> Multiply(this ReadOnlySpan<{typeName}> units, double multiplicator)
{
if (units.Length == 0) return [];
Span<{typeName}> result = new {typeName}[units.Length];
MultiplyCore(units, multiplicator, result);
return result;
}
public static Span<{typeName}> Multiply(this double multiplicator, ReadOnlySpan<{typeName}> units)
=> units.Multiply(multiplicator);
public static Span<{typeName}?> Multiply(this ReadOnlySpan<{typeName}?> units, double multiplicator)
{
if (units.Length == 0) return [];
Span<{typeName}?> result = new {typeName}?[units.Length];
units.CopyTo(result);
int len = result.Length;
for (int i = 0; i < len; i++)
{
ref var item = ref result[i];
if (item.HasValue) item = new {typeName}(item.Value._Value * multiplicator);
}
return result;
}
public static Span<{typeName}?> Multiply(this double multiplicator, ReadOnlySpan<{typeName}?> units)
=> units.Multiply(multiplicator);
// === List<Length> ===
public static List<{typeName}> Multiply(this List<{typeName}> units, double multiplicator)
{
if (units is null) return null!;
int len = units.Count;
if (len == 0) return [];
var result = new List<{typeName}>(len);
result.SetCountUnsafe(len);
MultiplyCore(CollectionsMarshal.AsSpan(units), multiplicator, CollectionsMarshal.AsSpan(result));
return result;
}
public static List<{typeName}> Multiply(this double multiplicator, List<{typeName}> units)
=> units.Multiply(multiplicator);
public static List<{typeName}?> Multiply(this List<{typeName}?> units, double multiplicator)
{
if (units is null) return null!;
int len = units.Count;
if (len == 0) return [];
var result = new List<{typeName}?>(units);
Span<{typeName}?> dstSpan = CollectionsMarshal.AsSpan(result);
for (int i = 0; i < len; i++)
{
ref var item = ref dstSpan[i];
if (item.HasValue) item = new {typeName}(item.Value._Value * multiplicator);
}
return result;
}
public static List<{typeName}?> Multiply(this double multiplicator, List<{typeName}?> units)
=> units.Multiply(multiplicator);
// === ICollection<Length> ===
public static Tcoll Multiply<Tcoll>(this ICollection<{typeName}> units, double multiplicator)
where Tcoll : class, ICollection<{typeName}>, new()
{
ArgumentNullException.ThrowIfNull(units);
var tColl = new Tcoll();
if (tColl is List<{typeName}> list)
list.Capacity = units.Count;
foreach (var item in units)
tColl.Add(new {typeName}(item._Value * multiplicator));
return tColl;
}
public static Tcoll Multiply<Tcoll>(this double multiplicator, ICollection<{typeName}> units)
where Tcoll : class, ICollection<{typeName}>, new() => Multiply<Tcoll>(units, multiplicator);
public static Tcoll Multiply<Tcoll>(this ICollection<{typeName}?> units, double multiplicator)
where Tcoll : class, ICollection<{typeName}?>, new()
{
ArgumentNullException.ThrowIfNull(units);
var tColl = new Tcoll();
if (tColl is List<{typeName}> list)
list.Capacity = units.Count;
foreach (var item in units)
tColl.Add(new {typeName}(item.HasValue ? item.Value._Value * multiplicator : 0d));
return tColl;
}
public static Tcoll Multiply<Tcoll>(this double multiplicator, ICollection<{typeName}?> units)
where Tcoll : class, ICollection<{typeName}?>, new() => Multiply<Tcoll>(units, multiplicator);
// === IEnumerable<Length> ===
public static IEnumerable<{typeName}> Multiply(this IEnumerable<{typeName}> units, double multiplicator)
{
ArgumentNullException.ThrowIfNull(units);
foreach (var item in units)
yield return new {typeName}(item._Value * multiplicator);
}
public static IEnumerable<{typeName}> Multiply(this double multiplicator, IEnumerable<{typeName}> units)
=> units.Multiply(multiplicator);
public static IEnumerable<{typeName}?> Multiply(this IEnumerable<{typeName}?> units, double multiplicator)
{
ArgumentNullException.ThrowIfNull(units);
foreach (var item in units)
yield return item.HasValue
? new {typeName}(item.Value._Value * multiplicator) : null;
}
public static IEnumerable<{typeName}?> Multiply(this double multiplicator, IEnumerable<{typeName}?> units)
=> units.Multiply(multiplicator);
// ==========================================
// === DIVIDE ===
// ==========================================
public static {typeName}[] Divide(this {typeName}[] units, double divisor)
{
if (units is null) return null!;
if (units.Length == 0) return [];
if (divisor == 0 || double.IsNaN(divisor))
throw new ArgumentException(""Делитель не может быть 0 или NaN."", nameof(divisor));
var result = new {typeName}[units.Length];
DivideCore(units, divisor, result);
return result;
}
public static {typeName}[] Divide(this double dividend, {typeName}[] units)
{
if (units is null) return null!;
if (units.Length == 0) return [];
var result = new {typeName}[units.Length];
DivideCore(dividend, units, result); // Вызов зеркального ядра (число / массив)
return result;
}
public static {typeName}?[] Divide(this {typeName}?[] units, double divisor)
{
if (units is null) return null!;
if (units.Length == 0) return [];
if (divisor == 0 || double.IsNaN(divisor)) throw new ArgumentException(""Делитель не может быть 0 или NaN."", nameof(divisor));
var result = ({typeName}?[])units.Clone();
int len = result.Length;
for (int i = 0; i < len; i++)
{
ref var item = ref result[i];
if (item.HasValue) item = new {typeName}(item.Value._Value / divisor);
}
return result;
}
public static {typeName}?[] Divide(this double dividend, {typeName}?[] units)
{
if (units is null) return null!;
if (units.Length == 0) return [];
var result = ({typeName}?[])units.Clone();
int len = result.Length;
for (int i = 0; i < len; i++)
{
ref var item = ref result[i];
if (item.HasValue) item = new {typeName}(dividend / item.Value._Value);
}
return result;
}
// === ReadOnlySpan ===
public static Span<{typeName}> Divide(this ReadOnlySpan<{typeName}> units, double divisor)
{
if (units.Length == 0) return [];
if (divisor == 0 || double.IsNaN(divisor)) throw new ArgumentException(""Делитель не может быть 0 или NaN."", nameof(divisor));
var result = new {typeName}[units.Length];
DivideCore(units, divisor, result);
return result;
}
public static Span<{typeName}> Divide(this double dividend, ReadOnlySpan<{typeName}> units)
{
if (units.Length == 0) return [];
var result = new {typeName}[units.Length];
DivideCore(dividend, units, result);
return result;
}
public static Span<{typeName}?> Divide(this ReadOnlySpan<{typeName}?> units, double divisor)
{
if (units.Length == 0) return [];
if (divisor == 0 || double.IsNaN(divisor))
throw new ArgumentException(""Делитель не может быть 0 или NaN."", nameof(divisor));
Span<{typeName}?> result = new {typeName}?[units.Length];
units.CopyTo(result);
int len = result.Length;
for (int i = 0; i < len; i++)
{
ref var item = ref result[i];
if (item.HasValue) item = new {typeName}(item.Value._Value / divisor);
}
return result;
}
public static Span<{typeName}?> Divide(this double dividend, ReadOnlySpan<{typeName}?> units)
{
if (units.Length == 0) return [];
Span<{typeName}?> result = new {typeName}?[units.Length];
units.CopyTo(result);
int len = result.Length;
for (int i = 0; i < len; i++)
{
ref var item = ref result[i];
if (item.HasValue) item = new {typeName}(dividend / item.Value._Value);
}
return result;
}
// === List<Length> ===
public static List<{typeName}> Divide(this List<{typeName}> units, double divisor)
{
if (units is null) return null!;
if (divisor == 0 || double.IsNaN(divisor)) throw new ArgumentException(""Делитель не может быть 0 или NaN."", nameof(divisor));
int len = units.Count;
if (len == 0) return [];
var result = new List<{typeName}>(len);
result.SetCountUnsafe(len);
DivideCore(CollectionsMarshal.AsSpan(units), divisor, CollectionsMarshal.AsSpan(result));
return result;
}
public static List<{typeName}> Divide(this double dividend, List<{typeName}> units)
{
if (units is null) return null!;
int len = units.Count;
if (len == 0) return [];
var result = new List<{typeName}>(len);
result.SetCountUnsafe(len);
DivideCore(dividend, CollectionsMarshal.AsSpan(units), CollectionsMarshal.AsSpan(result));
return result;
}
public static List<{typeName}?> Divide(this List<{typeName}?> units, double divisor)
{
if (units is null) return null!;
if (divisor == 0 || double.IsNaN(divisor)) throw new ArgumentException(""Делитель не может быть 0 или NaN."", nameof(divisor));
int len = units.Count;
if (len == 0) return [];
var result = new List<{typeName}?>(units);
Span<{typeName}?> dstSpan = CollectionsMarshal.AsSpan(result);
for (int i = 0; i < len; i++)
{
ref var item = ref dstSpan[i];
if (item.HasValue) item = new {typeName}(item.Value._Value / divisor);
}
return result;
}
public static List<{typeName}?> Divide(this double dividend, List<{typeName}?> units)
{
if (units is null) return null!;
int len = units.Count;
if (len == 0) return [];
var result = new List<{typeName}?>(units);
Span<{typeName}?> dstSpan = CollectionsMarshal.AsSpan(result);
for (int i = 0; i < len; i++)
{
ref var item = ref dstSpan[i];
if (item.HasValue) item = new {typeName}(dividend / item.Value._Value);
}
return result;
}
// === ICollection<Length> ===
public static Tcoll Divide<Tcoll>(this ICollection<{typeName}> units, double divisor)
where Tcoll : class, ICollection<{typeName}>, new()
{
ArgumentNullException.ThrowIfNull(units);
var tColl = new Tcoll();
if (tColl is List<{typeName}> list)
list.Capacity = units.Count;
foreach (var item in units)
tColl.Add(new {typeName}(item._Value / divisor));
return tColl;
}
public static Tcoll Divide<Tcoll>(this double dividend, ICollection<{typeName}> units)
where Tcoll : class, ICollection<{typeName}>, new()
{
ArgumentNullException.ThrowIfNull(units);
var tColl = new Tcoll();
if (tColl is List<{typeName}> list)
list.Capacity = units.Count;
foreach (var item in units)
tColl.Add(new {typeName}(dividend / item._Value));
return tColl;
}
public static Tcoll Divide<Tcoll>(this ICollection<{typeName}?> units, double divisor)
where Tcoll : class, ICollection<{typeName}?>, new()
{
ArgumentNullException.ThrowIfNull(units);
var tColl = new Tcoll();
if (tColl is List<{typeName}> list)
list.Capacity = units.Count;
foreach (var item in units)
tColl.Add(new {typeName}(item.HasValue ? item.Value._Value / divisor : 0d));
return tColl;
}
public static Tcoll Divide<Tcoll>(this double dividend, ICollection<{typeName}?> units)
where Tcoll : class, ICollection<{typeName}?>, new()
{
ArgumentNullException.ThrowIfNull(units);
var tColl = new Tcoll();
if (tColl is List<{typeName}> list)
list.Capacity = units.Count;
foreach (var item in units)
tColl.Add(new {typeName}(item.HasValue ? dividend / item.Value._Value : 0d));
return tColl;
}
// === IEnumerable<Length> ===
public static IEnumerable<{typeName}> Divide(this IEnumerable<{typeName}> units, double divisor)
{
ArgumentNullException.ThrowIfNull(units);
foreach (var item in units)
yield return new {typeName}(item._Value * divisor);
}
public static IEnumerable<{typeName}> Divide(this double dividend, IEnumerable<{typeName}> units)
{
ArgumentNullException.ThrowIfNull(units);
foreach (var item in units)
yield return new {typeName}(dividend / item._Value);
}
public static IEnumerable<{typeName}?> Divide(this IEnumerable<{typeName}?> units, double divisor)
{
ArgumentNullException.ThrowIfNull(units);
foreach (var item in units)
yield return item.HasValue
? new {typeName}(item.Value._Value * divisor) : null;
}
public static IEnumerable<{typeName}?> Divide(this double dividend, IEnumerable<{typeName}?> units)
{
ArgumentNullException.ThrowIfNull(units);
foreach (var item in units)
yield return item.HasValue
? new {typeName}(dividend / item.Value._Value) : null;
}
// ==========================================
// === PLUS ===
// ==========================================
public static {typeName}[] Plus(this {typeName}[] units, double summand)
{
if (units is null) return null!;
if (units.Length == 0) return [];
var result = new {typeName}[units.Length];
PlusCore(units, summand, result);
return result;
}
public static {typeName}[] Plus(this double summand, {typeName}[] units)
=> units.Plus(summand);
public static {typeName}?[] Plus(this {typeName}?[] units, double summand)
{
if (units is null) return null!;
if (units.Length == 0) return [];
var result = ({typeName}?[])units.Clone();
int len = result.Length;
for (int i = 0; i < len; i++)
{
ref var item = ref result[i];
if (item.HasValue) item = new {typeName}(item.Value._Value + summand);
}
return result;
}
public static {typeName}?[] Plus(this double summand, {typeName}?[] units)
=> units.Plus(summand);
// === ReadOnlySpan ===
public static Span<{typeName}> Plus(this ReadOnlySpan<{typeName}> units, double summand)
{
if (units.Length == 0) return [];
var result = new {typeName}[units.Length];
PlusCore(units, summand, result);
return result;
}
public static Span<{typeName}> Plus(this double summand, ReadOnlySpan<{typeName}> units)
=> units.Plus(summand);
public static Span<{typeName}?> Plus(this ReadOnlySpan<{typeName}?> units, double summand)
{
if (units.Length == 0) return [];
Span<{typeName}?> result = new {typeName}?[units.Length];
units.CopyTo(result);
int len = result.Length;
for (int i = 0; i < len; i++)
{
ref var item = ref result[i];
if (item.HasValue) item = new {typeName}(item.Value._Value + summand);
}
return result;
}
public static Span<{typeName}?> Plus(this double summand, ReadOnlySpan<{typeName}?> units)
=> units.Multiply(summand);
// === List<Length> ===
public static List<{typeName}> Plus(this List<{typeName}> units, double summand)
{
if (units is null) return null!;
int len = units.Count;
if (len == 0) return [];
var result = new List<{typeName}>(len);
result.SetCountUnsafe(len);
PlusCore(CollectionsMarshal.AsSpan(units), summand, CollectionsMarshal.AsSpan(result));
return result;
}
public static List<{typeName}> Plus(this double summand, List<{typeName}> units)
=> units.Plus(summand);
public static List<{typeName}?> Plus(this List<{typeName}?> units, double summand)
{
if (units is null) return null!;
int len = units.Count;
if (len == 0) return [];
var result = new List<{typeName}?>(units);
Span<{typeName}?> dstSpan = CollectionsMarshal.AsSpan(result);
for (int i = 0; i < len; i++)
{
ref var item = ref dstSpan[i];
if (item.HasValue) item = new {typeName}(item.Value._Value + summand);
}
return result;
}
public static List<{typeName}?> Plus(this double summand, List<{typeName}?> units)
=> units.Plus(summand);
// === ICollection<Length> ===
public static Tcoll Plus<Tcoll>(this ICollection<{typeName}> units, double summand)
where Tcoll : class, ICollection<{typeName}>, new()
{
ArgumentNullException.ThrowIfNull(units);
var tColl = new Tcoll();
if (tColl is List<{typeName}> list)
list.Capacity = units.Count;
foreach (var item in units)
tColl.Add(new {typeName}(item._Value + summand));
return tColl;
}
public static Tcoll Plus<Tcoll>(this double summand, ICollection<{typeName}> units)
where Tcoll : class, ICollection<{typeName}>, new() => Plus<Tcoll>(units, summand);
public static Tcoll Plus<Tcoll>(this ICollection<{typeName}?> units, double summand)
where Tcoll : class, ICollection<{typeName}?>, new()
{
ArgumentNullException.ThrowIfNull(units);
var tColl = new Tcoll();
if (tColl is List<{typeName}> list)
list.Capacity = units.Count;
foreach (var item in units)
tColl.Add(new {typeName}(item.HasValue ? item.Value._Value + summand : 0d));
return tColl;
}
public static Tcoll Plus<Tcoll>(this double summand, ICollection<{typeName}?> units)
where Tcoll : class, ICollection<{typeName}?>, new() => Plus<Tcoll>(units, summand);
// === IEnumerable<Length> ===
public static IEnumerable<{typeName}> Plus(this IEnumerable<{typeName}> units, double summand)
{
ArgumentNullException.ThrowIfNull(units);
foreach (var item in units)
yield return new {typeName}(item._Value + summand);
}
public static IEnumerable<{typeName}> Plus(this double summand, IEnumerable<{typeName}> units)
=> units.Plus(summand);
public static IEnumerable<{typeName}?> Plus(this IEnumerable<{typeName}?> units, double summand)
{
ArgumentNullException.ThrowIfNull(units);
foreach (var item in units)
yield return item.HasValue
? new {typeName}(item.Value._Value + summand) : null;
}
public static IEnumerable<{typeName}?> Plus(this double summand, IEnumerable<{typeName}?> units)
=> units.Plus(summand);
// ==========================================
// === MINUS ===
// ==========================================
public static {typeName}[] Minus(this {typeName}[] units, double subtrahend)
{
if (units is null) return null!;
if (units.Length == 0) return [];
var result = new {typeName}[units.Length];
MinusCore(units, subtrahend, result);
return result;
}
public static {typeName}[] Minus(this double minuend, {typeName}[] units)
{
if (units is null) return null!;
if (units.Length == 0) return [];
var result = new {typeName}[units.Length];
MinusCore(minuend, units, result); // Вызов зеркального ядра (число - массив)
return result;
}
public static {typeName}?[] Minus(this {typeName}?[] units, double subtrahend)
{
if (units is null) return null!;
if (units.Length == 0) return [];
var result = ({typeName}?[])units.Clone();
int len = result.Length;
for (int i = 0; i < len; i++)
{
ref var item = ref result[i];
if (item.HasValue) item = new {typeName}(item.Value._Value - subtrahend);
}
return result;
}
public static {typeName}?[] Minus(this double minuend, {typeName}?[] units)
{
if (units is null) return null!;
if (units.Length == 0) return [];
var result = ({typeName}?[])units.Clone();
int len = result.Length;
for (int i = 0; i < len; i++)
{
ref var item = ref result[i];
if (item.HasValue) item = new {typeName}(minuend - item.Value._Value);
}
return result;
}
// === ReadOnlySpan ===
public static Span<{typeName}> Minus(this ReadOnlySpan<{typeName}> units, double subtrahend)
{
if (units.Length == 0) return [];
var result = new {typeName}[units.Length];
MinusCore(units, subtrahend, result);
return result;
}
public static Span<{typeName}> Minus(this double minuend, ReadOnlySpan<{typeName}> units)
{
if (units.Length == 0) return [];
var result = new {typeName}[units.Length];
MinusCore(minuend, units, result);
return result;
}
public static Span<{typeName}?> Minus(this ReadOnlySpan<{typeName}?> units, double subtrahend)
{
if (units.Length == 0) return [];
Span<{typeName}?> result = new {typeName}?[units.Length];
units.CopyTo(result);
int len = result.Length;
for (int i = 0; i < len; i++)
{
ref var item = ref result[i];
if (item.HasValue) item = new {typeName}(item.Value._Value - subtrahend);
}
return result;
}
public static Span<{typeName}?> Minus(this double minuend, ReadOnlySpan<{typeName}?> units)
{
if (units.Length == 0) return [];
Span<{typeName}?> result = new {typeName}?[units.Length];
units.CopyTo(result);
int len = result.Length;
for (int i = 0; i < len; i++)
{
ref var item = ref result[i];
if (item.HasValue) item = new {typeName}(minuend - item.Value._Value);
}
return result;
}
// === List<Length> ===
public static List<{typeName}> Minus(this List<{typeName}> units, double subtrahend)
{
if (units is null) return null!;
int len = units.Count;
if (len == 0) return [];
var result = new List<{typeName}>(len);
result.SetCountUnsafe(len);
MinusCore(CollectionsMarshal.AsSpan(units), subtrahend, CollectionsMarshal.AsSpan(result));
return result;
}
public static List<{typeName}> Minus(this double minuend, List<{typeName}> units)
{
if (units is null) return null!;
int len = units.Count;
if (len == 0) return [];
var result = new List<{typeName}>(len);
result.SetCountUnsafe(len);
MinusCore(minuend, CollectionsMarshal.AsSpan(units), CollectionsMarshal.AsSpan(result));
return result;
}
public static List<{typeName}?> Minus(this List<{typeName}?> units, double subtrahend)
{
if (units is null) return null!;
int len = units.Count;
if (len == 0) return [];
var result = new List<{typeName}?>(units);
Span<{typeName}?> dstSpan = CollectionsMarshal.AsSpan(result);
for (int i = 0; i < len; i++)
{
ref var item = ref dstSpan[i];
if (item.HasValue) item = new {typeName}(item.Value._Value - subtrahend);
}
return result;
}
public static List<{typeName}?> Minus(this double minuend, List<{typeName}?> units)
{
if (units is null) return null!;
int len = units.Count;
if (len == 0) return [];
var result = new List<{typeName}?>(units);
Span<{typeName}?> dstSpan = CollectionsMarshal.AsSpan(result);
for (int i = 0; i < len; i++)
{
ref var item = ref dstSpan[i];
if (item.HasValue) item = new {typeName}(minuend - item.Value._Value);
}
return result;
}
// === ICollection<Length> ===
public static Tcoll Minus<Tcoll>(this ICollection<{typeName}> units, double subtrahend)
where Tcoll : class, ICollection<{typeName}>, new()
{
ArgumentNullException.ThrowIfNull(units);
var tColl = new Tcoll();
if (tColl is List<{typeName}> list)
list.Capacity = units.Count;
foreach (var item in units)
tColl.Add(new {typeName}(item._Value - subtrahend));
return tColl;
}
public static Tcoll Minus<Tcoll>(this double minuend, ICollection<{typeName}> units)
where Tcoll : class, ICollection<{typeName}>, new()
{
ArgumentNullException.ThrowIfNull(units);
var tColl = new Tcoll();
if (tColl is List<{typeName}> list)
list.Capacity = units.Count;
foreach (var item in units)
tColl.Add(new {typeName}(minuend - item._Value));
return tColl;
}
public static Tcoll Minus<Tcoll>(this ICollection<{typeName}?> units, double subtrahend)
where Tcoll : class, ICollection<{typeName}?>, new()
{
ArgumentNullException.ThrowIfNull(units);
var tColl = new Tcoll();
if (tColl is List<{typeName}> list)
list.Capacity = units.Count;
foreach (var item in units)
tColl.Add(new {typeName}(item.HasValue ? item.Value._Value - subtrahend : 0d));
return tColl;
}
public static Tcoll Minus<Tcoll>(this double minuend, ICollection<{typeName}?> units)
where Tcoll : class, ICollection<{typeName}?>, new()
{
ArgumentNullException.ThrowIfNull(units);
var tColl = new Tcoll();
if (tColl is List<{typeName}> list)
list.Capacity = units.Count;
foreach (var item in units)
tColl.Add(new {typeName}(item.HasValue ? minuend - item.Value._Value : 0d));
return tColl;
}
// === IEnumerable<Length> ===
public static IEnumerable<{typeName}> Minus(this IEnumerable<{typeName}> units, double subtrahend)
{
ArgumentNullException.ThrowIfNull(units);
foreach (var item in units)
yield return new {typeName}(item._Value - subtrahend);
}
public static IEnumerable<{typeName}> Minus(this double minuend, IEnumerable<{typeName}> units)
{
ArgumentNullException.ThrowIfNull(units);
foreach (var item in units)
yield return new {typeName}(minuend - item._Value);
}
public static IEnumerable<{typeName}?> Minus(this IEnumerable<{typeName}?> units, double subtrahend)
{
ArgumentNullException.ThrowIfNull(units);
foreach (var item in units)
yield return item.HasValue
? new {typeName}(item.Value._Value - subtrahend) : null;
}
public static IEnumerable<{typeName}?> Minus(this double minuend, IEnumerable<{typeName}?> units)
{
ArgumentNullException.ThrowIfNull(units);
foreach (var item in units)
yield return item.HasValue
? new {typeName}(minuend - item.Value._Value) : null;
}
// ==========================================
// ЦЕЛАЯ СТЕПЕНЬ (int power)
// ==========================================
public static {typeName}[] Pow(this {typeName}[] units, int power)
{
if (units is null) return null!;
if (units.Length == 0) return [];
var result = new {typeName}[units.Length];
PowCore(units, power, result);
return result;
}
public static {typeName}?[] Pow(this {typeName}?[] units, int power)
{
if (units is null) return null!;
if (units.Length == 0) return [];
var result = ({typeName}?[])units.Clone();
int len = result.Length;
for (int i = 0; i < len; i++)
{
ref var item = ref result[i];
if (item.HasValue)
{
// Используем тот же быстрый QuickPow, что и в ядре, чтобы не вызывать тяжелый Math.Pow
item = new {typeName}(item.Value._Value.QuickPow(power));
}
}
return result;
}
// === ReadOnlySpan ===
public static Span<{typeName}> Pow(this ReadOnlySpan<{typeName}> units, int power)
{
if (units.Length == 0) return [];
var result = new {typeName}[units.Length];
PowCore(units, power, result);
return result;
}
public static Span<{typeName}?> Pow(this ReadOnlySpan<{typeName}?> units, int power)
{
int len = units.Length;
if (len == 0) return [];
Span<{typeName}?> result = new {typeName}?[units.Length];
units.CopyTo(result);
for (int i = 0; i < len; i++)
{
ref var item = ref result[i];
if (item.HasValue) item = new {typeName}(item.Value._Value.QuickPow(power));
}
return result;
}
// === List<Length> ===
public static List<{typeName}> Pow(this List<{typeName}> units, int power)
{
if (units is null) return null!;
int len = units.Count;
if (len == 0) return [];
var result = new List<{typeName}>(len);
result.SetCountUnsafe(len);
PowCore(CollectionsMarshal.AsSpan(units), power, CollectionsMarshal.AsSpan(result));
return result;
}
public static List<{typeName}?> Pow(this List<{typeName}?> units, int power)
{
if (units is null) return null!;
int len = units.Count;
if (len == 0) return [];
var result = new List<{typeName}?>(units);
Span<{typeName}?> dstSpan = CollectionsMarshal.AsSpan(result);
for (int i = 0; i < len; i++)
{
ref var item = ref dstSpan[i];
if (item.HasValue) item = new {typeName}(item.Value._Value.QuickPow(power));
}
return result;
}
// === ICollection<Length> ===
public static Tcoll Pow<Tcoll>(this ICollection<{typeName}> units, int power)
where Tcoll : class, ICollection<{typeName}>, new()
{
ArgumentNullException.ThrowIfNull(units);
var tColl = new Tcoll();
if (tColl is List<{typeName}> list)
list.Capacity = units.Count;
foreach (var item in units)
tColl.Add(new {typeName}(item._Value.QuickPow(power)));
return tColl;
}
public static Tcoll Pow<Tcoll>(this ICollection<{typeName}?> units, int power)
where Tcoll : class, ICollection<{typeName}?>, new()
{
ArgumentNullException.ThrowIfNull(units);
var tColl = new Tcoll();
if (tColl is List<{typeName}> list)
list.Capacity = units.Count;
foreach (var item in units)
tColl.Add(new {typeName}(item.HasValue ? item.Value._Value.QuickPow(power) : 0d));
return tColl;
}
// === IEnumerable<Length> ===
public static IEnumerable<{typeName}> Pow(this IEnumerable<{typeName}> units, int power)
{
ArgumentNullException.ThrowIfNull(units);
foreach (var item in units)
yield return new {typeName}(item._Value.QuickPow(power));
}
public static IEnumerable<{typeName}?> Pow(this IEnumerable<{typeName}?> units, int power)
{
ArgumentNullException.ThrowIfNull(units);
foreach (var item in units)
yield return item.HasValue
? new {typeName}(item.Value._Value.QuickPow(power)) : null;
}
// ==========================================
// ДРОБНАЯ СТЕПЕНЬ (double power)
// ==========================================
public static {typeName}[] Pow(this {typeName}[] units, double power)
{
if (units is null) return null!;
if (units.Length == 0) return [];
var result = new {typeName}[units.Length];
PowCore(units, power, result);
return result;
}
public static {typeName}?[] Pow(this {typeName}?[] units, double power)
{
if (units is null) return null!;
if (units.Length == 0) return [];
var result = ({typeName}?[])units.Clone();
int len = result.Length;
for (int i = 0; i < len; i++)
{
ref var item = ref result[i];
if (item.HasValue)
{
item = new {typeName}(Math.Pow(item.Value._Value, power));
}
}
return result;
}
// === ReadOnlySpan ===
public static Span<{typeName}> Pow(this ReadOnlySpan<{typeName}> units, double power)
{
if (units.Length == 0) return [];
var result = new {typeName}[units.Length];
PowCore(units, power, result);
return result;
}
public static Span<{typeName}?> Pow(this ReadOnlySpan<{typeName}?> units, double power)
{
int len = units.Length;
if (len == 0) return [];
Span<{typeName}?> result = new {typeName}?[units.Length];
units.CopyTo(result);
for (int i = 0; i < len; i++)
{
ref var item = ref result[i];
if (item.HasValue) item = new {typeName}(Math.Pow(item.Value._Value, power));
}
return result;
}
// === List<Length> ===
public static List<{typeName}> Pow(this List<{typeName}> units, double power)
{
if (units is null) return null!;
int len = units.Count;
if (len == 0) return [];
var result = new List<{typeName}>(len);
result.SetCountUnsafe(len);
PowCore(CollectionsMarshal.AsSpan(units), power, CollectionsMarshal.AsSpan(result));
return result;
}
public static List<{typeName}?> Pow(this List<{typeName}?> units, double power)
{
if (units is null) return null!;
int len = units.Count;
if (len == 0) return [];
var result = new List<{typeName}?>(units);
Span<{typeName}?> dstSpan = CollectionsMarshal.AsSpan(result);
for (int i = 0; i < len; i++)
{
ref var item = ref dstSpan[i];
if (item.HasValue) item = new {typeName}(Math.Pow(item.Value._Value, power));
}
return result;
}
// === ICollection<Length> ===
public static Tcoll Pow<Tcoll>(this ICollection<{typeName}> units, double power)
where Tcoll : class, ICollection<{typeName}>, new()
{
ArgumentNullException.ThrowIfNull(units);
var tColl = new Tcoll();
if (tColl is List<{typeName}> list)
list.Capacity = units.Count;
foreach (var item in units)
tColl.Add(new {typeName}(Math.Pow(item._Value, power)));
return tColl;
}
public static Tcoll Pow<Tcoll>(this ICollection<{typeName}?> units, double power)
where Tcoll : class, ICollection<{typeName}?>, new()
{
ArgumentNullException.ThrowIfNull(units);
var tColl = new Tcoll();
if (tColl is List<{typeName}> list)
list.Capacity = units.Count;
foreach (var item in units)
tColl.Add(new {typeName}(item.HasValue ? Math.Pow(item.Value._Value, power) : 0d));
return tColl;
}
// === IEnumerable<Length> ===
public static IEnumerable<{typeName}> Pow(this IEnumerable<{typeName}> units, double power)
{
ArgumentNullException.ThrowIfNull(units);
foreach (var item in units)
yield return new {typeName}(Math.Pow(item._Value, power));
}
public static IEnumerable<{typeName}?> Pow(this IEnumerable<{typeName}?> units, double power)
{
ArgumentNullException.ThrowIfNull(units);
foreach (var item in units)
yield return item.HasValue
? new {typeName}(Math.Pow(item.Value._Value, power)) : null;
}
// ==========================================
// КВАДРАТНЫЙ КОРЕНЬ (Sqrt)
// ==========================================
public static {typeName}[] Sqrt(this {typeName}[] units)
{
if (units is null) return null!;
if (units.Length == 0) return [];
var result = new {typeName}[units.Length];
SqrtCore(units, result);
return result;
}
public static {typeName}?[] Sqrt(this {typeName}?[] units)
{
if (units is null) return null!;
if (units.Length == 0) return [];
var result = ({typeName}?[])units.Clone();
int len = result.Length;
for (int i = 0; i < len; i++)
{
ref var item = ref result[i];
if (item.HasValue)
{
item = new {typeName}(Math.Sqrt(item.Value._Value));
}
}
return result;
}
// === ReadOnlySpan ===
public static Span<{typeName}> Sqrt(this ReadOnlySpan<{typeName}> units)
{
if (units.Length == 0) return [];
var result = new {typeName}[units.Length];
SqrtCore(units, result);
return result;
}
public static Span<{typeName}?> Sqrt(this ReadOnlySpan<{typeName}?> units)
{
int len = units.Length;
if (len == 0) return [];
Span<{typeName}?> result = new {typeName}?[units.Length];
units.CopyTo(result);
for (int i = 0; i < len; i++)
{
ref var item = ref result[i];
if (item.HasValue) item = new {typeName}(Math.Sqrt(item.Value._Value));
}
return result;
}
// === List<Length> ===
public static List<{typeName}> Sqrt(this List<{typeName}> units)
{
if (units is null) return null!;
int len = units.Count;
if (len == 0) return [];
var result = new List<{typeName}>(len);
result.SetCountUnsafe(len);
SqrtCore(CollectionsMarshal.AsSpan(units), CollectionsMarshal.AsSpan(result));
return result;
}
public static List<{typeName}?> Sqrt(this List<{typeName}?> units)
{
if (units is null) return null!;
int len = units.Count;
if (len == 0) return [];
var result = new List<{typeName}?>(units);
Span<{typeName}?> dstSpan = CollectionsMarshal.AsSpan(result);
for (int i = 0; i < len; i++)
{
ref var item = ref dstSpan[i];
if (item.HasValue) item = new {typeName}(Math.Sqrt(item.Value._Value));
}
return result;
}
// === ICollection<Length> ===
public static Tcoll Pow<Tcoll>(this ICollection<{typeName}> units)
where Tcoll : class, ICollection<{typeName}>, new()
{
ArgumentNullException.ThrowIfNull(units);
var tColl = new Tcoll();
if (tColl is List<{typeName}> list)
list.Capacity = units.Count;
foreach (var item in units)
tColl.Add(new {typeName}(Math.Sqrt(item._Value)));
return tColl;
}
public static Tcoll Pow<Tcoll>(this ICollection<{typeName}?> units)
where Tcoll : class, ICollection<{typeName}?>, new()
{
ArgumentNullException.ThrowIfNull(units);
var tColl = new Tcoll();
if (tColl is List<{typeName}> list)
list.Capacity = units.Count;
foreach (var item in units)
tColl.Add(new {typeName}(item.HasValue ? Math.Sqrt(item.Value._Value) : 0d));
return tColl;
}
// === IEnumerable<Length> ===
public static IEnumerable<{typeName}> Pow(this IEnumerable<{typeName}> units)
{
ArgumentNullException.ThrowIfNull(units);
foreach (var item in units)
yield return new {typeName}(Math.Sqrt(item._Value));
}
public static IEnumerable<{typeName}?> Pow(this IEnumerable<{typeName}?> units)
{
ArgumentNullException.ThrowIfNull(units);
foreach (var item in units)
yield return item.HasValue
? new {typeName}(Math.Sqrt(item.Value._Value)) : null;
}
}
public class {typeName}Converter : JsonConverter<{typeName}>
{
// Используем инвариантную культуру, чтобы разделителем всегда была точка (10.5, а не 10,5)
private static readonly CultureInfo Culture = CultureInfo.InvariantCulture;
public override {typeName} Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
double doubleValue;
if (reader.TokenType == JsonTokenType.String)
{
// Безопасно парсим double из строки с поддержкой точки как разделителя
if (!double.TryParse(reader.GetString(), NumberStyles.Float, Culture, out doubleValue))
{
throw new JsonException($""Не удалось преобразовать строковое значение в double для метрики {nameof({typeName})}."");
}
}
else
{
// Прямое быстрое чтение числа из JSON
doubleValue = reader.GetDouble();
}
return new(doubleValue);
}
public override void Write(Utf8JsonWriter writer, {typeName} value, JsonSerializerOptions options)
{
// Записываем число напрямую в байтовый буфер без выделения памяти под строки
writer.WriteNumberValue(value._Value);
}
public override void WriteAsPropertyName(Utf8JsonWriter writer, {typeName} value, JsonSerializerOptions options)
{
// Ключи JSON-объектов всегда должны быть строками.
// Форматируем double в строку с точкой, чтобы другие сервисы экосистемы прочитали её корректно.
// Формат ""R"" (Round-trip) гарантирует, что число не потеряет точность при обратном парсинге.
writer.WritePropertyName(value._Value.ToString(""R"", Culture));
}
public override {typeName} ReadAsPropertyName(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
string propertyName = reader.GetString()!;
if (!double.TryParse(propertyName, NumberStyles.Float, Culture, out double doubleValue))
{
throw new JsonException($""Невалидное числовое значение в ключе свойства JSON: '{propertyName}' для метрики {nameof({typeName})}."");
}
return new(doubleValue);
}
}
";
return skeleton.Replace("{typeName}", typeName).Replace("{ns}", ns);
}
private readonly struct StructInfo(string ns, string name)
{
public string Namespace { get; } = ns;
public string TypeName { get; } = name;
}
}