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)); /// C^2 = this^2 + B^2 C = (this^2 + B^2).Sqrt(2) public {typeName} HypFromLeg({typeName} B) => new(Math.Sqrt(_Value * _Value + B._Value * B._Value)); /// C^2 = this^2 + B^2 C = (this^2 + B^2).Sqrt(2) public {typeName} HypFromLeg({typeName}? B) { double b = B is null ? 0d : B.Value._Value; return new(Math.Sqrt(_Value * _Value + b * b)); } /// C^2 = this^2 + B^2 B = (C^2 - this^2).Sqrt(2) public {typeName} LegFromHyp({typeName} C) => new(Math.Sqrt(C._Value * C._Value - _Value * _Value)); /// C^2 = this^2 + B^2 B = (C^2 - this^2).Sqrt(2) public {typeName} LegFromHyp({typeName}? C) { double c = C is null ? 0d : C.Value._Value; return new(Math.Sqrt(c * c - _Value * _Value)); } /// this^2 = A^2 + B^2 B = (this^2 - A^2).Sqrt(2) public {typeName} LegFromLeg({typeName} A) => new(Math.Sqrt(_Value * _Value - A._Value * A._Value)); /// this^2 = A^2 + B^2 B = (this^2 - A^2).Sqrt(2) 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 srcDouble = MemoryMarshal.Cast<{typeName}, double>(source); Span dstDouble = MemoryMarshal.Cast<{typeName}, double>(destination); var vectorizedValue = new Vector(value); int vectorSize = Vector.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 srcWindow = MemoryMarshal.CreateReadOnlySpan(ref currentSrc, vectorSize); var vector = new Vector(srcWindow); var multiplied = vector * vectorizedValue; ref double currentDst = ref Unsafe.Add(ref dstRef, i); Span 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 srcDouble = MemoryMarshal.Cast<{typeName}, double>(source); Span dstDouble = MemoryMarshal.Cast<{typeName}, double>(destination); var vectorizedValue = new Vector(divisor); int vectorSize = Vector.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 srcWindow = MemoryMarshal.CreateReadOnlySpan(ref currentSrc, vectorSize); var vector = new Vector(srcWindow); var multiplied = vector / vectorizedValue; ref double currentDst = ref Unsafe.Add(ref dstRef, i); Span 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 srcDouble = MemoryMarshal.Cast<{typeName}, double>(source); Span dstDouble = MemoryMarshal.Cast<{typeName}, double>(destination); var vectorizedValue = new Vector(dividend); var zeroVector = Vector.Zero; // Вектор из нулей для сравнения int vectorSize = Vector.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 srcWindow = MemoryMarshal.CreateReadOnlySpan(ref currentSrc, vectorSize); var vector = new Vector(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 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 srcDouble = MemoryMarshal.Cast<{typeName}, double>(source); Span dstDouble = MemoryMarshal.Cast<{typeName}, double>(destination); var vectorizedValue = new Vector(summand); int vectorSize = Vector.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 srcWindow = MemoryMarshal.CreateReadOnlySpan(ref currentSrc, vectorSize); var vector = new Vector(srcWindow); var multiplied = vector + vectorizedValue; ref double currentDst = ref Unsafe.Add(ref dstRef, i); Span 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 srcDouble = MemoryMarshal.Cast<{typeName}, double>(source); Span dstDouble = MemoryMarshal.Cast<{typeName}, double>(destination); var vectorizedValue = new Vector(subtrahend); int vectorSize = Vector.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 srcWindow = MemoryMarshal.CreateReadOnlySpan(ref currentSrc, vectorSize); var vector = new Vector(srcWindow); var multiplied = vector - vectorizedValue; ref double currentDst = ref Unsafe.Add(ref dstRef, i); Span 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 srcDouble = MemoryMarshal.Cast<{typeName}, double>(source); Span dstDouble = MemoryMarshal.Cast<{typeName}, double>(destination); var vectorizedValue = new Vector(minuend); int vectorSize = Vector.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 srcWindow = MemoryMarshal.CreateReadOnlySpan(ref currentSrc, vectorSize); var vector = new Vector(srcWindow); var multiplied = vectorizedValue - vector; ref double currentDst = ref Unsafe.Add(ref dstRef, i); Span 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 srcDouble = MemoryMarshal.Cast<{typeName}, double>(source); Span dstDouble = MemoryMarshal.Cast<{typeName}, double>(destination); int vectorSize = Vector.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(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 VectorPow(Vector baseVector, int exp) { if (exp == 0) return Vector.One; if (exp == 1) return baseVector; if (exp < 0) { baseVector = Vector.One / baseVector; exp = -exp; // Внимание: может переполниться при int.MinValue, но для степеней это редчайший кейс } var result = Vector.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 srcDouble = MemoryMarshal.Cast<{typeName}, double>(source); Span dstDouble = MemoryMarshal.Cast<{typeName}, double>(destination); int vectorSize = Vector.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 srcWindow = MemoryMarshal.CreateReadOnlySpan(ref currentSrc, vectorSize); var vector = new Vector(srcWindow); var multiplied = Vector.SquareRoot(vector); ref double currentDst = ref Unsafe.Add(ref dstRef, i); Span 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 === 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 === public static Tcoll Multiply(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(this double multiplicator, ICollection<{typeName}> units) where Tcoll : class, ICollection<{typeName}>, new() => Multiply(units, multiplicator); public static Tcoll Multiply(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(this double multiplicator, ICollection<{typeName}?> units) where Tcoll : class, ICollection<{typeName}?>, new() => Multiply(units, multiplicator); // === IEnumerable === 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 === 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 === public static Tcoll Divide(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(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(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(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 === 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 === 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 === public static Tcoll Plus(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(this double summand, ICollection<{typeName}> units) where Tcoll : class, ICollection<{typeName}>, new() => Plus(units, summand); public static Tcoll Plus(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(this double summand, ICollection<{typeName}?> units) where Tcoll : class, ICollection<{typeName}?>, new() => Plus(units, summand); // === IEnumerable === 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 === 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 === public static Tcoll Minus(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(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(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(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 === 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 === 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 === public static Tcoll Pow(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(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 === 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 === 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 === public static Tcoll Pow(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(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 === 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 === 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 === public static Tcoll Pow(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(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 === 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; } }