1784 lines
74 KiB
C#
1784 lines
74 KiB
C#
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 = @"
|
||
using System.Globalization;
|
||
using System.Runtime.Serialization;
|
||
|
||
namespace QWERTYkez.Mensura.Units;
|
||
|
||
[JsonConverter(typeof({typeName}Converter))]
|
||
public readonly partial record struct {typeName}
|
||
{
|
||
[JsonInclude, DataMember, JsonPropertyName(""v"")] // для JSON / EF на случай сбоев, если пробелма с _Value
|
||
internal double 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 => _Value < 0;
|
||
[JsonIgnore, IgnoreDataMember] public bool IsZero => _Value == 0;
|
||
|
||
|
||
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}Extension
|
||
{
|
||
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;
|
||
}
|
||
} |