From e43c885fbebf7126b52573ddeb83595297e90bda Mon Sep 17 00:00:00 2001 From: Fantom TM Date: Mon, 1 Jun 2026 01:31:31 +0700 Subject: [PATCH] +++ --- .../OperatorsGenerator.cs | 424 +- .../UnitOperatorsGenerator.cs | 105 +- QWERTYkez.Mensura/Extensions.cs | 3661 ++++++++++++++++- .../Units/Pogon/PogonXXXXXXXX.Gen.cs | 17 +- .../Units/Pogon/PogonXXXXXXXX.Ref.cs | 6 +- QWERTYkez.Mensura/Units/XXXXXXXX.Gen.cs | 3049 ++++++-------- QWERTYkez.Mensura/Units/XXXXXXXX.Ref.cs | 122 +- 7 files changed, 5146 insertions(+), 2238 deletions(-) diff --git a/QWERTYkez.Mensura.Generator/OperatorsGenerator.cs b/QWERTYkez.Mensura.Generator/OperatorsGenerator.cs index c8925fd..deb9f04 100644 --- a/QWERTYkez.Mensura.Generator/OperatorsGenerator.cs +++ b/QWERTYkez.Mensura.Generator/OperatorsGenerator.cs @@ -1,226 +1,226 @@ -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.Text; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Linq; -using System.Text; +//using Microsoft.CodeAnalysis; +//using Microsoft.CodeAnalysis.CSharp.Syntax; +//using Microsoft.CodeAnalysis.Text; +//using System.Collections.Generic; +//using System.Collections.Immutable; +//using System.Linq; +//using System.Text; -namespace QWERTYkez.Mensura.Generator; +//namespace QWERTYkez.Mensura.Generator; -[Generator(LanguageNames.CSharp)] -internal class CollectionsOperatorsGenerator : IIncrementalGenerator -{ - private const string AttributeShortName = "CollectionsOperatorsGenerator"; - private const string AttributeFullName = AttributeShortName + "Attribute"; +//[Generator(LanguageNames.CSharp)] +//internal class CollectionsOperatorsGenerator : IIncrementalGenerator +//{ +// private const string AttributeShortName = "CollectionsOperatorsGenerator"; +// private const string AttributeFullName = AttributeShortName + "Attribute"; - private const string AttributeSource = @"namespace QWERTYkez.Mensura; +// private const string AttributeSource = @"namespace QWERTYkez.Mensura; -[System.AttributeUsage(System.AttributeTargets.Struct | System.AttributeTargets.Method, AllowMultiple = false, Inherited = false)] -internal sealed class CollectionsOperatorsGeneratorAttribute : System.Attribute { }"; +//[System.AttributeUsage(System.AttributeTargets.Struct | System.AttributeTargets.Method, AllowMultiple = false, Inherited = false)] +//internal sealed class CollectionsOperatorsGeneratorAttribute : System.Attribute { }"; - public void Initialize(IncrementalGeneratorInitializationContext context) - { - context.RegisterPostInitializationOutput(context => - context.AddSource( - $"{AttributeFullName}.g", - SourceText.From(AttributeSource, Encoding.UTF8))); +// public void Initialize(IncrementalGeneratorInitializationContext context) +// { +// context.RegisterPostInitializationOutput(context => +// context.AddSource( +// $"{AttributeFullName}.g", +// SourceText.From(AttributeSource, Encoding.UTF8))); - var operatorsPipeline = - context.SyntaxProvider.CreateSyntaxProvider>>( - (node, _) => - { - if (node is ClassDeclarationSyntax cds) - { - SyntaxNode sn = cds; - while (sn.Parent is not null && - sn.Parent is not FileScopedNamespaceDeclarationSyntax) - { - sn = sn.Parent; - } - if (sn.Parent is FileScopedNamespaceDeclarationSyntax - && cds.TypeParameterList is null - && - ((cds.Members.OfType() - .Where(m => m.AttributeLists.SelectMany(al => al.Attributes) - .Any(a => a.Name.GetText().ToString().Contains(AttributeShortName))) - .Any()) - || - (cds.Modifiers.Any(m => m.Text == "partial") - && cds.AttributeLists - .SelectMany(al => al.Attributes) - .Any(a => a.Name - .GetText() - .ToString() - .Contains(AttributeShortName))))) - { - return true; - } - } - return false; - }, - (syntax, _) => - { - string nameSpace; - var cds = (ClassDeclarationSyntax)syntax.Node; - { - SyntaxNode sn = cds; - while (sn.Parent is not FileScopedNamespaceDeclarationSyntax) - { - sn = sn.Parent!; - } - var nds = (FileScopedNamespaceDeclarationSyntax)sn.Parent; - nameSpace = nds.Name.ToString(); - } - var Res = new StringBuilder(); - { - Res.Append(cds.Modifiers); - Res.Append(" class "); - Res.Append(cds.Identifier.Text); - Res.Append(" "); - Res.Append(cds.BaseList); - Res.Append(" "); - Res.Append(cds.ConstraintClauses); - } +// var operatorsPipeline = +// context.SyntaxProvider.CreateSyntaxProvider>>( +// (node, _) => +// { +// if (node is ClassDeclarationSyntax cds) +// { +// SyntaxNode sn = cds; +// while (sn.Parent is not null && +// sn.Parent is not FileScopedNamespaceDeclarationSyntax) +// { +// sn = sn.Parent; +// } +// if (sn.Parent is FileScopedNamespaceDeclarationSyntax +// && cds.TypeParameterList is null +// && +// ((cds.Members.OfType() +// .Where(m => m.AttributeLists.SelectMany(al => al.Attributes) +// .Any(a => a.Name.GetText().ToString().Contains(AttributeShortName))) +// .Any()) +// || +// (cds.Modifiers.Any(m => m.Text == "partial") +// && cds.AttributeLists +// .SelectMany(al => al.Attributes) +// .Any(a => a.Name +// .GetText() +// .ToString() +// .Contains(AttributeShortName))))) +// { +// return true; +// } +// } +// return false; +// }, +// (syntax, _) => +// { +// string nameSpace; +// var cds = (ClassDeclarationSyntax)syntax.Node; +// { +// SyntaxNode sn = cds; +// while (sn.Parent is not FileScopedNamespaceDeclarationSyntax) +// { +// sn = sn.Parent!; +// } +// var nds = (FileScopedNamespaceDeclarationSyntax)sn.Parent; +// nameSpace = nds.Name.ToString(); +// } +// var Res = new StringBuilder(); +// { +// Res.Append(cds.Modifiers); +// Res.Append(" class "); +// Res.Append(cds.Identifier.Text); +// Res.Append(" "); +// Res.Append(cds.BaseList); +// Res.Append(" "); +// Res.Append(cds.ConstraintClauses); +// } - var operators = cds.Members.OfType() - .Where(m => m.AttributeLists - .SelectMany(al => al.Attributes) - .Any(a => a.Name.GetText().ToString().Contains(AttributeShortName))) - .Where(mb => mb.ParameterList.Parameters.Count == 2) - .Select(mb => new Operation() - { - ReturnType = mb.ReturnType.ToString(), - OperatorToken = mb.OperatorToken.Text, - TypeA = mb.ParameterList.Parameters[0].Type!.ToString(), - TypeB = mb.ParameterList.Parameters[1].Type!.ToString(), - }); - return new(new(nameSpace, cds.Identifier.Text, Res.ToString()), [.. operators]); - }) - .Collect(); +// var operators = cds.Members.OfType() +// .Where(m => m.AttributeLists +// .SelectMany(al => al.Attributes) +// .Any(a => a.Name.GetText().ToString().Contains(AttributeShortName))) +// .Where(mb => mb.ParameterList.Parameters.Count == 2) +// .Select(mb => new Operation() +// { +// ReturnType = mb.ReturnType.ToString(), +// OperatorToken = mb.OperatorToken.Text, +// TypeA = mb.ParameterList.Parameters[0].Type!.ToString(), +// TypeB = mb.ParameterList.Parameters[1].Type!.ToString(), +// }); +// return new(new(nameSpace, cds.Identifier.Text, Res.ToString()), [.. operators]); +// }) +// .Collect(); - context.RegisterSourceOutput(operatorsPipeline, GenerateOperators); - } +// context.RegisterSourceOutput(operatorsPipeline, GenerateOperators); +// } - readonly static string[] CollectionTypes = ["MetricArray", "MetricList", "MetricObservableCollection"]; - static void GenerateOperators(SourceProductionContext context, ImmutableArray>> pairs) - { - foreach (var ng in pairs.GroupBy(c => c.Key.NameSpace)) - { - StringBuilder document = new("namespace "); - document.Append(ng.Key); - document.Append(";"); +// readonly static string[] CollectionTypes = ["MetricArray", "MetricList", "MetricObservableCollection"]; +// static void GenerateOperators(SourceProductionContext context, ImmutableArray>> pairs) +// { +// foreach (var ng in pairs.GroupBy(c => c.Key.NameSpace)) +// { +// StringBuilder document = new("namespace "); +// document.Append(ng.Key); +// document.Append(";"); - var classes = ng.ToList().Select(c => (ClassData?)c.Key).ToList(); - var operations = ng.ToList().SelectMany(c => c.Value).ToList(); - var multiplications = operations.Where(op => op.OperatorToken == "*").ToList(); - var divisions = operations.Where(op => op.OperatorToken == "/").ToList(); +// var classes = ng.ToList().Select(c => (ClassData?)c.Key).ToList(); +// var operations = ng.ToList().SelectMany(c => c.Value).ToList(); +// var multiplications = operations.Where(op => op.OperatorToken == "*").ToList(); +// var divisions = operations.Where(op => op.OperatorToken == "/").ToList(); - foreach (var ops in multiplications.GroupBy(op => op.TypeA)) - { - var Class = classes.FirstOrDefault(cl => cl!.Value.ClassName == ops.Key); - if (Class is not null) - { - document.AppendLine(); - document.AppendLine(); - document.AppendLine(Class.Value.ClassHeader); - document.AppendLine("{"); - foreach (var op in ops) - foreach (var ct in CollectionTypes) - { - document.AppendLine(@$" - public static {ct}<{op.ReturnType}> operator *({op.TypeA} left, {ct}<{op.TypeB}> right) => right.MetricSelect(UU => left * UU); - public static {ct}<{op.ReturnType}> operator *({ct}<{op.TypeB}> left, {op.TypeA} right) => left.MetricSelect(UU => UU * right); -"); - } - document.Append("}"); - } - else - { - context.ReportDiagnostic(Diagnostic.Create(new( - "MSG0001", - "Need a class with an attribute", - $"It is necessary to have a empty partial class \"{ops.Key}\" with the attribute \"{AttributeShortName}\"", - "category", - DiagnosticSeverity.Error, - true), null, ops.Key)); - } - } +// foreach (var ops in multiplications.GroupBy(op => op.TypeA)) +// { +// var Class = classes.FirstOrDefault(cl => cl!.Value.ClassName == ops.Key); +// if (Class is not null) +// { +// document.AppendLine(); +// document.AppendLine(); +// document.AppendLine(Class.Value.ClassHeader); +// document.AppendLine("{"); +// foreach (var op in ops) +// foreach (var ct in CollectionTypes) +// { +// document.AppendLine(@$" +// public static {ct}<{op.ReturnType}> operator *({op.TypeA} left, {ct}<{op.TypeB}> right) => right.MetricSelect(UU => left * UU); +// public static {ct}<{op.ReturnType}> operator *({ct}<{op.TypeB}> left, {op.TypeA} right) => left.MetricSelect(UU => UU * right); +//"); +// } +// document.Append("}"); +// } +// else +// { +// context.ReportDiagnostic(Diagnostic.Create(new( +// "MSG0001", +// "Need a class with an attribute", +// $"It is necessary to have a empty partial class \"{ops.Key}\" with the attribute \"{AttributeShortName}\"", +// "category", +// DiagnosticSeverity.Error, +// true), null, ops.Key)); +// } +// } - foreach (var ops in divisions.GroupBy(op => op.TypeA)) - { - var Class = classes.FirstOrDefault(cl => cl!.Value.ClassName == ops.Key); - if (Class is not null) - { - document.AppendLine(); - document.AppendLine(); - document.AppendLine(Class.Value.ClassHeader); - document.AppendLine("{"); - foreach (var op in ops) - foreach (var ct in CollectionTypes) - { - document.AppendLine(@$" - public static {ct}<{op.ReturnType}> operator /({op.TypeA} left, {ct}<{op.TypeB}> right) => right.MetricSelect(UU => left / UU); -"); - } - document.Append("}"); - } - else - { - context.ReportDiagnostic(Diagnostic.Create(new( - "MSG0001", - "Need a class with an attribute", - $"It is necessary to have a empty partial class \"{ops.Key}\" with the attribute \"{AttributeShortName}\"", - "category", - DiagnosticSeverity.Error, - true), null, ops.Key)); - } - } - foreach (var ops in divisions.GroupBy(op => op.TypeB)) - { - var Class = classes.FirstOrDefault(cl => cl!.Value.ClassName == ops.Key); - if (Class is not null) - { - document.AppendLine(); - document.AppendLine(); - document.AppendLine(Class.Value.ClassHeader); - document.AppendLine("{"); - foreach (var op in ops) - foreach (var ct in CollectionTypes) - { - document.AppendLine(@$" - public static {ct}<{op.ReturnType}> operator /({ct}<{op.TypeA}> left, {op.TypeB} right) => left.MetricSelect(UU => UU / right); -"); - } - document.Append("}"); - } - else - { - context.ReportDiagnostic(Diagnostic.Create(new( - "MSG0001", - "Need a class with an attribute", - $"It is necessary to have a empty partial class \"{ops.Key}\" with the attribute \"{AttributeShortName}\"", - "category", - DiagnosticSeverity.Error, - true), null, ops.Key)); - } - } +// foreach (var ops in divisions.GroupBy(op => op.TypeA)) +// { +// var Class = classes.FirstOrDefault(cl => cl!.Value.ClassName == ops.Key); +// if (Class is not null) +// { +// document.AppendLine(); +// document.AppendLine(); +// document.AppendLine(Class.Value.ClassHeader); +// document.AppendLine("{"); +// foreach (var op in ops) +// foreach (var ct in CollectionTypes) +// { +// document.AppendLine(@$" +// public static {ct}<{op.ReturnType}> operator /({op.TypeA} left, {ct}<{op.TypeB}> right) => right.MetricSelect(UU => left / UU); +//"); +// } +// document.Append("}"); +// } +// else +// { +// context.ReportDiagnostic(Diagnostic.Create(new( +// "MSG0001", +// "Need a class with an attribute", +// $"It is necessary to have a empty partial class \"{ops.Key}\" with the attribute \"{AttributeShortName}\"", +// "category", +// DiagnosticSeverity.Error, +// true), null, ops.Key)); +// } +// } +// foreach (var ops in divisions.GroupBy(op => op.TypeB)) +// { +// var Class = classes.FirstOrDefault(cl => cl!.Value.ClassName == ops.Key); +// if (Class is not null) +// { +// document.AppendLine(); +// document.AppendLine(); +// document.AppendLine(Class.Value.ClassHeader); +// document.AppendLine("{"); +// foreach (var op in ops) +// foreach (var ct in CollectionTypes) +// { +// document.AppendLine(@$" +// public static {ct}<{op.ReturnType}> operator /({ct}<{op.TypeA}> left, {op.TypeB} right) => left.MetricSelect(UU => UU / right); +//"); +// } +// document.Append("}"); +// } +// else +// { +// context.ReportDiagnostic(Diagnostic.Create(new( +// "MSG0001", +// "Need a class with an attribute", +// $"It is necessary to have a empty partial class \"{ops.Key}\" with the attribute \"{AttributeShortName}\"", +// "category", +// DiagnosticSeverity.Error, +// true), null, ops.Key)); +// } +// } - context.AddSource($"operations.{ng.Key}.g", document.ToString()); - } - } -} +// context.AddSource($"operations.{ng.Key}.g", document.ToString()); +// } +// } +//} -public struct ClassData(string nameSpace, string className, string classHeader) -{ - public string NameSpace = nameSpace; - public string ClassName = className; - public string ClassHeader = classHeader; -} -public struct Operation(string operatorToken, string typeA, string typeB, string returnType) -{ - public string ReturnType = returnType; - public string OperatorToken = operatorToken; - public string TypeA = typeA; - public string TypeB = typeB; -} \ No newline at end of file +//public struct ClassData(string nameSpace, string className, string classHeader) +//{ +// public string NameSpace = nameSpace; +// public string ClassName = className; +// public string ClassHeader = classHeader; +//} +//public struct Operation(string operatorToken, string typeA, string typeB, string returnType) +//{ +// public string ReturnType = returnType; +// public string OperatorToken = operatorToken; +// public string TypeA = typeA; +// public string TypeB = typeB; +//} \ No newline at end of file diff --git a/QWERTYkez.Mensura.Generator/UnitOperatorsGenerator.cs b/QWERTYkez.Mensura.Generator/UnitOperatorsGenerator.cs index e188d8a..22f10dd 100644 --- a/QWERTYkez.Mensura.Generator/UnitOperatorsGenerator.cs +++ b/QWERTYkez.Mensura.Generator/UnitOperatorsGenerator.cs @@ -87,12 +87,12 @@ namespace QWERTYkez.Mensura string ns = info.Namespace; // Здесь должен быть полный код из вашего файла XXXXXXXXXXXX.cs - // с заменой XXXXXXXXXXXXXX на {typeName}. Для краткости приведён скелет. + // с заменой {typeName} на {typeName}. Для краткости приведён скелет. // Вы должны скопировать сюда всё содержимое вашего второго файла, - // заменив XXXXXXXXXXXXXX на {typeName}. + // заменив {typeName} на {typeName}. string skeleton = @" global using {typeName} = QWERTYkez.Mensura.Units.{typeName}; -global using {typeName}Extensions = QWERTYkez.Mensura.Units.{typeName}Extensions; +global using {typeName}Extensions = QWERTYkez.Mensura.Units.{typeName}Extension; global using {typeName}Converter = QWERTYkez.Mensura.Units.{typeName}Converter; using System.Globalization; @@ -118,13 +118,8 @@ public readonly partial record struct {typeName} : IMensuraUnit<{typeName}>, IEq [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 IsNegative => _Value < 0; [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); @@ -308,19 +303,19 @@ public readonly partial record struct {typeName} : IMensuraUnit<{typeName}>, IEq } } -public static class {typeName}Extensions +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(units?.Sum(m => m._Value) ?? 0d); + public static {typeName} MetricAverage(this IEnumerable<{typeName}> units) => new(units?.Average(m => m._Value) ?? double.NaN); + public static {typeName} MetricMax(this IEnumerable<{typeName}> units) => new(units?.Max(m => m._Value) ?? double.MinValue); + public static {typeName} MetricMin(this IEnumerable<{typeName}> units) => new(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 }; + public static {typeName} MetricSum(this IEnumerable<{typeName}?> units) => new(units?.Sum(m => m.ToDouble()) ?? 0d); + public static {typeName} MetricAverage(this IEnumerable<{typeName}?> units) => new(units?.Average(m => m.ToDouble()) ?? double.NaN); + public static {typeName} MetricMax(this IEnumerable<{typeName}?> units) => new(units?.Max(m => m.ToDouble()) ?? double.MinValue); + public static {typeName} MetricMin(this IEnumerable<{typeName}?> units) => new(units?.Min(m => m.ToDouble()) ?? double.MaxValue); internal static void MultiplyCore(ReadOnlySpan<{typeName}> source, double value, Span<{typeName}> destination) @@ -329,7 +324,7 @@ public static class {typeName}Extensions ReadOnlySpan srcDouble = MemoryMarshal.Cast<{typeName}, double>(source); Span dstDouble = MemoryMarshal.Cast<{typeName}, double>(destination); - var vectorizedValue = new Vector(value); + var vectorized_Value = new Vector(value); int vectorSize = Vector.Count; int i = 0; @@ -342,7 +337,7 @@ public static class {typeName}Extensions 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; + var multiplied = vector * vectorized_Value; ref double currentDst = ref Unsafe.Add(ref dstRef, i); Span dstWindow = MemoryMarshal.CreateSpan(ref currentDst, vectorSize); multiplied.CopyTo(dstWindow); @@ -363,7 +358,7 @@ public static class {typeName}Extensions ReadOnlySpan srcDouble = MemoryMarshal.Cast<{typeName}, double>(source); Span dstDouble = MemoryMarshal.Cast<{typeName}, double>(destination); - var vectorizedValue = new Vector(divisor); + var vectorized_Value = new Vector(divisor); int vectorSize = Vector.Count; int i = 0; @@ -376,7 +371,7 @@ public static class {typeName}Extensions 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; + var multiplied = vector / vectorized_Value; ref double currentDst = ref Unsafe.Add(ref dstRef, i); Span dstWindow = MemoryMarshal.CreateSpan(ref currentDst, vectorSize); multiplied.CopyTo(dstWindow); @@ -393,7 +388,7 @@ public static class {typeName}Extensions ReadOnlySpan srcDouble = MemoryMarshal.Cast<{typeName}, double>(source); Span dstDouble = MemoryMarshal.Cast<{typeName}, double>(destination); - var vectorizedValue = new Vector(dividend); + var vectorized_Value = new Vector(dividend); var zeroVector = Vector.Zero; // Вектор из нулей для сравнения int vectorSize = Vector.Count; int i = 0; @@ -414,7 +409,7 @@ public static class {typeName}Extensions throw new DivideByZeroException($""Обнаружен делитель, равный нулю, в районе индексов {i}..{i + vectorSize - 1}.""); } - var multiplied = vectorizedValue / vector; + var multiplied = vectorized_Value / vector; ref double currentDst = ref Unsafe.Add(ref dstRef, i); Span dstWindow = MemoryMarshal.CreateSpan(ref currentDst, vectorSize); multiplied.CopyTo(dstWindow); @@ -437,7 +432,7 @@ public static class {typeName}Extensions ReadOnlySpan srcDouble = MemoryMarshal.Cast<{typeName}, double>(source); Span dstDouble = MemoryMarshal.Cast<{typeName}, double>(destination); - var vectorizedValue = new Vector(summand); + var vectorized_Value = new Vector(summand); int vectorSize = Vector.Count; int i = 0; @@ -450,7 +445,7 @@ public static class {typeName}Extensions 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; + var multiplied = vector + vectorized_Value; ref double currentDst = ref Unsafe.Add(ref dstRef, i); Span dstWindow = MemoryMarshal.CreateSpan(ref currentDst, vectorSize); multiplied.CopyTo(dstWindow); @@ -467,7 +462,7 @@ public static class {typeName}Extensions ReadOnlySpan srcDouble = MemoryMarshal.Cast<{typeName}, double>(source); Span dstDouble = MemoryMarshal.Cast<{typeName}, double>(destination); - var vectorizedValue = new Vector(subtrahend); + var vectorized_Value = new Vector(subtrahend); int vectorSize = Vector.Count; int i = 0; @@ -480,7 +475,7 @@ public static class {typeName}Extensions 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; + var multiplied = vector - vectorized_Value; ref double currentDst = ref Unsafe.Add(ref dstRef, i); Span dstWindow = MemoryMarshal.CreateSpan(ref currentDst, vectorSize); multiplied.CopyTo(dstWindow); @@ -497,7 +492,7 @@ public static class {typeName}Extensions ReadOnlySpan srcDouble = MemoryMarshal.Cast<{typeName}, double>(source); Span dstDouble = MemoryMarshal.Cast<{typeName}, double>(destination); - var vectorizedValue = new Vector(minuend); + var vectorized_Value = new Vector(minuend); int vectorSize = Vector.Count; int i = 0; @@ -510,7 +505,7 @@ public static class {typeName}Extensions 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; + var multiplied = vectorized_Value - vector; ref double currentDst = ref Unsafe.Add(ref dstRef, i); Span dstWindow = MemoryMarshal.CreateSpan(ref currentDst, vectorSize); multiplied.CopyTo(dstWindow); @@ -550,7 +545,7 @@ public static class {typeName}Extensions if (exp < 0) { baseVector = Vector.One / baseVector; - exp = -exp; // Внимание: может переполниться при int.MinValue, но для степеней это редчайший кейс + exp = -exp; // Внимание: может переполниться при int.Min_Value, но для степеней это редчайший кейс } var result = Vector.One; @@ -731,7 +726,7 @@ public static class {typeName}Extensions public static Tcoll Multiply(this ICollection<{typeName}> units, double multiplicator) where Tcoll : class, ICollection<{typeName}>, new() { - ArgumentNullException.ThrowIfNull(units); + if (units is null) return null!; var tColl = new Tcoll(); if (tColl is List<{typeName}> list) list.Capacity = units.Count; @@ -744,7 +739,7 @@ public static class {typeName}Extensions public static Tcoll Multiply(this ICollection<{typeName}?> units, double multiplicator) where Tcoll : class, ICollection<{typeName}?>, new() { - ArgumentNullException.ThrowIfNull(units); + if (units is null) return null!; var tColl = new Tcoll(); if (tColl is List<{typeName}> list) list.Capacity = units.Count; @@ -936,7 +931,7 @@ public static class {typeName}Extensions public static Tcoll Divide(this ICollection<{typeName}> units, double divisor) where Tcoll : class, ICollection<{typeName}>, new() { - ArgumentNullException.ThrowIfNull(units); + if (units is null) return null!; var tColl = new Tcoll(); if (tColl is List<{typeName}> list) list.Capacity = units.Count; @@ -947,7 +942,7 @@ public static class {typeName}Extensions public static Tcoll Divide(this double dividend, ICollection<{typeName}> units) where Tcoll : class, ICollection<{typeName}>, new() { - ArgumentNullException.ThrowIfNull(units); + if (units is null) return null!; var tColl = new Tcoll(); if (tColl is List<{typeName}> list) list.Capacity = units.Count; @@ -958,7 +953,7 @@ public static class {typeName}Extensions public static Tcoll Divide(this ICollection<{typeName}?> units, double divisor) where Tcoll : class, ICollection<{typeName}?>, new() { - ArgumentNullException.ThrowIfNull(units); + if (units is null) return null!; var tColl = new Tcoll(); if (tColl is List<{typeName}> list) list.Capacity = units.Count; @@ -969,7 +964,7 @@ public static class {typeName}Extensions public static Tcoll Divide(this double dividend, ICollection<{typeName}?> units) where Tcoll : class, ICollection<{typeName}?>, new() { - ArgumentNullException.ThrowIfNull(units); + if (units is null) return null!; var tColl = new Tcoll(); if (tColl is List<{typeName}> list) list.Capacity = units.Count; @@ -1101,7 +1096,7 @@ public static class {typeName}Extensions public static Tcoll Plus(this ICollection<{typeName}> units, double summand) where Tcoll : class, ICollection<{typeName}>, new() { - ArgumentNullException.ThrowIfNull(units); + if (units is null) return null!; var tColl = new Tcoll(); if (tColl is List<{typeName}> list) list.Capacity = units.Count; @@ -1114,7 +1109,7 @@ public static class {typeName}Extensions public static Tcoll Plus(this ICollection<{typeName}?> units, double summand) where Tcoll : class, ICollection<{typeName}?>, new() { - ArgumentNullException.ThrowIfNull(units); + if (units is null) return null!; var tColl = new Tcoll(); if (tColl is List<{typeName}> list) list.Capacity = units.Count; @@ -1297,7 +1292,7 @@ public static class {typeName}Extensions public static Tcoll Minus(this ICollection<{typeName}> units, double subtrahend) where Tcoll : class, ICollection<{typeName}>, new() { - ArgumentNullException.ThrowIfNull(units); + if (units is null) return null!; var tColl = new Tcoll(); if (tColl is List<{typeName}> list) list.Capacity = units.Count; @@ -1308,7 +1303,7 @@ public static class {typeName}Extensions public static Tcoll Minus(this double minuend, ICollection<{typeName}> units) where Tcoll : class, ICollection<{typeName}>, new() { - ArgumentNullException.ThrowIfNull(units); + if (units is null) return null!; var tColl = new Tcoll(); if (tColl is List<{typeName}> list) list.Capacity = units.Count; @@ -1319,7 +1314,7 @@ public static class {typeName}Extensions public static Tcoll Minus(this ICollection<{typeName}?> units, double subtrahend) where Tcoll : class, ICollection<{typeName}?>, new() { - ArgumentNullException.ThrowIfNull(units); + if (units is null) return null!; var tColl = new Tcoll(); if (tColl is List<{typeName}> list) list.Capacity = units.Count; @@ -1330,7 +1325,7 @@ public static class {typeName}Extensions public static Tcoll Minus(this double minuend, ICollection<{typeName}?> units) where Tcoll : class, ICollection<{typeName}?>, new() { - ArgumentNullException.ThrowIfNull(units); + if (units is null) return null!; var tColl = new Tcoll(); if (tColl is List<{typeName}> list) list.Capacity = units.Count; @@ -1453,7 +1448,7 @@ public static class {typeName}Extensions public static Tcoll Pow(this ICollection<{typeName}> units, int power) where Tcoll : class, ICollection<{typeName}>, new() { - ArgumentNullException.ThrowIfNull(units); + if (units is null) return null!; var tColl = new Tcoll(); if (tColl is List<{typeName}> list) list.Capacity = units.Count; @@ -1464,7 +1459,7 @@ public static class {typeName}Extensions public static Tcoll Pow(this ICollection<{typeName}?> units, int power) where Tcoll : class, ICollection<{typeName}?>, new() { - ArgumentNullException.ThrowIfNull(units); + if (units is null) return null!; var tColl = new Tcoll(); if (tColl is List<{typeName}> list) list.Capacity = units.Count; @@ -1573,7 +1568,7 @@ public static class {typeName}Extensions public static Tcoll Pow(this ICollection<{typeName}> units, double power) where Tcoll : class, ICollection<{typeName}>, new() { - ArgumentNullException.ThrowIfNull(units); + if (units is null) return null!; var tColl = new Tcoll(); if (tColl is List<{typeName}> list) list.Capacity = units.Count; @@ -1584,7 +1579,7 @@ public static class {typeName}Extensions public static Tcoll Pow(this ICollection<{typeName}?> units, double power) where Tcoll : class, ICollection<{typeName}?>, new() { - ArgumentNullException.ThrowIfNull(units); + if (units is null) return null!; var tColl = new Tcoll(); if (tColl is List<{typeName}> list) list.Capacity = units.Count; @@ -1694,7 +1689,7 @@ public static class {typeName}Extensions public static Tcoll Pow(this ICollection<{typeName}> units) where Tcoll : class, ICollection<{typeName}>, new() { - ArgumentNullException.ThrowIfNull(units); + if (units is null) return null!; var tColl = new Tcoll(); if (tColl is List<{typeName}> list) list.Capacity = units.Count; @@ -1705,7 +1700,7 @@ public static class {typeName}Extensions public static Tcoll Pow(this ICollection<{typeName}?> units) where Tcoll : class, ICollection<{typeName}?>, new() { - ArgumentNullException.ThrowIfNull(units); + if (units is null) return null!; var tColl = new Tcoll(); if (tColl is List<{typeName}> list) list.Capacity = units.Count; @@ -1737,12 +1732,12 @@ public class {typeName}Converter : JsonConverter<{typeName}> public override {typeName} Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { - double doubleValue; + double double_Value; if (reader.TokenType == JsonTokenType.String) { // Безопасно парсим double из строки с поддержкой точки как разделителя - if (!double.TryParse(reader.GetString(), NumberStyles.Float, Culture, out doubleValue)) + if (!double.TryParse(reader.GetString(), NumberStyles.Float, Culture, out double_Value)) { throw new JsonException($""Не удалось преобразовать строковое значение в double для метрики {nameof({typeName})}.""); } @@ -1750,10 +1745,10 @@ public class {typeName}Converter : JsonConverter<{typeName}> else { // Прямое быстрое чтение числа из JSON - doubleValue = reader.GetDouble(); + double_Value = reader.GetDouble(); } - return new(doubleValue); + return new(double_Value); } public override void Write(Utf8JsonWriter writer, {typeName} value, JsonSerializerOptions options) @@ -1774,12 +1769,12 @@ public class {typeName}Converter : JsonConverter<{typeName}> { string propertyName = reader.GetString()!; - if (!double.TryParse(propertyName, NumberStyles.Float, Culture, out double doubleValue)) + if (!double.TryParse(propertyName, NumberStyles.Float, Culture, out double double_Value)) { throw new JsonException($""Невалидное числовое значение в ключе свойства JSON: '{propertyName}' для метрики {nameof({typeName})}.""); } - return new(doubleValue); + return new(double_Value); } } "; diff --git a/QWERTYkez.Mensura/Extensions.cs b/QWERTYkez.Mensura/Extensions.cs index a255ae9..7ab69cf 100644 --- a/QWERTYkez.Mensura/Extensions.cs +++ b/QWERTYkez.Mensura/Extensions.cs @@ -1,4 +1,8 @@ -namespace QWERTYkez.Mensura; +using System.Buffers; +using System.Runtime.Intrinsics; +using System.Runtime.Intrinsics.X86; + +namespace QWERTYkez.Mensura; public static partial class Extensions { @@ -66,6 +70,3222 @@ public static partial class Extensions + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static double ToDouble(this T unit) + where T : struct, IMensuraUnit, IEquatable + => Unsafe.As(ref unit); + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static double ToDouble(this T? unit) + where T : struct, IMensuraUnit, IEquatable + { + T actual = unit.GetValueOrDefault(); + return Unsafe.As(ref actual); + } + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T ToUnit(this double val) + where T : struct, IMensuraUnit, IEquatable + => Unsafe.As(ref val); + + + // ========================================== + // Sum Average Max Min (не nullable) + // ========================================== + + public static T Sum(this ReadOnlySpan units) + where T : struct, IMensuraUnit, IEquatable + { + if (Unsafe.SizeOf() != sizeof(double)) + { + throw new InvalidOperationException($"Тип {typeof(T).Name} имеет неверный размер для SIMD."); + } + + if (units.IsEmpty) return default; + + ReadOnlySpan values = MemoryMarshal.Cast(units); + double sum = 0; + int i = 0; + + // 1. ПУТЬ AVX2 (Intel/AMD) — Самый быстрый, обрабатывает по 4 элемента через Unsafe + if (Avx2.IsSupported && values.Length >= 4) + { + Vector256 vSum = Vector256.Zero; + ref double start = ref MemoryMarshal.GetReference(values); + + for (; i <= values.Length - 4; i += 4) + { + ref double currentRef = ref Unsafe.Add(ref start, i); + var v = Unsafe.As>(ref currentRef); + vSum = Avx2.Add(vSum, v); + } + sum = vSum.GetElement(0) + vSum.GetElement(1) + vSum.GetElement(2) + vSum.GetElement(3); + } + // 2. ПУТЬ AVX (Старые x64 CPU) — Чуть медленнее AVX2, но тоже по 4 элемента + else if (Avx.IsSupported && values.Length >= 4) + { + Vector256 vSum = Vector256.Zero; + ref double start = ref MemoryMarshal.GetReference(values); + + for (; i <= values.Length - 4; i += 4) + { + ref double currentRef = ref Unsafe.Add(ref start, i); + var v = Unsafe.As>(ref currentRef); + vSum = Avx.Add(vSum, v); + } + sum = vSum.GetElement(0) + vSum.GetElement(1) + vSum.GetElement(2) + vSum.GetElement(3); + } + // 3. ПУТЬ VECTOR (ARM64 / Apple Silicon / SSE) — Кроссплатформенный SIMD fallback + else if (Vector.IsHardwareAccelerated && values.Length >= Vector.Count) + { + int vCount = Vector.Count; + Vector vSum = Vector.Zero; + + for (; i <= values.Length - vCount; i += vCount) + { + var v = new Vector(values.Slice(i, vCount)); + vSum += v; + } + + for (int j = 0; j < vCount; j++) + { + sum += vSum[j]; + } + } + + // Хвост массива (или обычный расчет, если SIMD вообще нет) + for (; i < values.Length; i++) + { + sum += values[i]; + } + + return Unsafe.As(ref sum); + } + public static T Average(this ReadOnlySpan units) + where T : struct, IMensuraUnit, IEquatable + { + // 0. Защитная проверка для JIT (0 рантайм оверхеда) + if (Unsafe.SizeOf() != sizeof(double)) + { + throw new InvalidOperationException($"Тип {typeof(T).Name} имеет неверный размер для SIMD."); + } + + // Если коллекция пустая, возвращаем структуру, содержащую double.NaN + if (units.IsEmpty) + { + double nan = double.NaN; + return Unsafe.As(ref nan); + } + + ReadOnlySpan values = MemoryMarshal.Cast(units); + double sum = 0; + int i = 0; + + // 1. ПУТЬ AVX2 (Intel/AMD) + if (Avx2.IsSupported && values.Length >= 4) + { + Vector256 vSum = Vector256.Zero; + ref double start = ref MemoryMarshal.GetReference(values); + + for (; i <= values.Length - 4; i += 4) + { + ref double currentRef = ref Unsafe.Add(ref start, i); + var v = Unsafe.As>(ref currentRef); + vSum = Avx2.Add(vSum, v); + } + sum = vSum.GetElement(0) + vSum.GetElement(1) + vSum.GetElement(2) + vSum.GetElement(3); + } + // 2. ПУТЬ AVX (Старые x64 CPU) + else if (Avx.IsSupported && values.Length >= 4) + { + Vector256 vSum = Vector256.Zero; + ref double start = ref MemoryMarshal.GetReference(values); + + for (; i <= values.Length - 4; i += 4) + { + ref double currentRef = ref Unsafe.Add(ref start, i); + var v = Unsafe.As>(ref currentRef); + vSum = Avx.Add(vSum, v); + } + sum = vSum.GetElement(0) + vSum.GetElement(1) + vSum.GetElement(2) + vSum.GetElement(3); + } + // 3. ПУТЬ VECTOR (ARM64 / SSE) + else if (Vector.IsHardwareAccelerated && values.Length >= Vector.Count) + { + int vCount = Vector.Count; + Vector vSum = Vector.Zero; + + for (; i <= values.Length - vCount; i += vCount) + { + var v = new Vector(values.Slice(i, vCount)); + vSum += v; + } + + for (int j = 0; j < vCount; j++) + { + sum += vSum[j]; + } + } + + // 4. Довычисление хвоста + for (; i < values.Length; i++) + { + sum += values[i]; + } + + // Находим среднее значение + double average = sum / values.Length; + + // Упаковываем double обратно в структуру T (zero-cost) + return Unsafe.As(ref average); + } + public static T Max(this ReadOnlySpan units) + where T : struct, IMensuraUnit, IEquatable + { + if (Unsafe.SizeOf() != sizeof(double)) + { + throw new InvalidOperationException($"Тип {typeof(T).Name} имеет неверный размер для SIMD."); + } + + if (units.IsEmpty) + { + double minVal = double.MinValue; + return Unsafe.As(ref minVal); + } + + ReadOnlySpan values = MemoryMarshal.Cast(units); + double max = double.MinValue; + int i = 0; + + if (Avx2.IsSupported && values.Length >= 4) + { + Vector256 vMax = Vector256.Create(double.MinValue); + ref double start = ref MemoryMarshal.GetReference(values); + + for (; i <= values.Length - 4; i += 4) + { + ref double currentRef = ref Unsafe.Add(ref start, i); + var v = Unsafe.As>(ref currentRef); + vMax = Avx2.Max(vMax, v); + } + max = Math.Max(Math.Max(vMax.GetElement(0), vMax.GetElement(1)), Math.Max(vMax.GetElement(2), vMax.GetElement(3))); + } + else if (Avx.IsSupported && values.Length >= 4) + { + Vector256 vMax = Vector256.Create(double.MinValue); + ref double start = ref MemoryMarshal.GetReference(values); + + for (; i <= values.Length - 4; i += 4) + { + ref double currentRef = ref Unsafe.Add(ref start, i); + var v = Unsafe.As>(ref currentRef); + vMax = Avx.Max(vMax, v); + } + max = Math.Max(Math.Max(vMax.GetElement(0), vMax.GetElement(1)), Math.Max(vMax.GetElement(2), vMax.GetElement(3))); + } + else if (Vector.IsHardwareAccelerated && values.Length >= Vector.Count) + { + int vCount = Vector.Count; + var vMax = new Vector(double.MinValue); + + for (; i <= values.Length - vCount; i += vCount) + { + var v = new Vector(values.Slice(i, vCount)); + vMax = Vector.Max(vMax, v); + } + + for (int j = 0; j < vCount; j++) + { + if (vMax[j] > max) max = vMax[j]; + } + } + + // Довычисление хвоста + for (; i < values.Length; i++) + { + if (values[i] > max) max = values[i]; + } + + return Unsafe.As(ref max); + } + public static T Min(this ReadOnlySpan units) + where T : struct, IMensuraUnit, IEquatable + { + if (Unsafe.SizeOf() != sizeof(double)) + { + throw new InvalidOperationException($"Тип {typeof(T).Name} имеет неверный размер для SIMD."); + } + + if (units.IsEmpty) + { + double maxVal = double.MaxValue; + return Unsafe.As(ref maxVal); + } + + ReadOnlySpan values = MemoryMarshal.Cast(units); + double min = double.MaxValue; + int i = 0; + + if (Avx2.IsSupported && values.Length >= 4) + { + Vector256 vMin = Vector256.Create(double.MaxValue); + ref double start = ref MemoryMarshal.GetReference(values); + + for (; i <= values.Length - 4; i += 4) + { + ref double currentRef = ref Unsafe.Add(ref start, i); + var v = Unsafe.As>(ref currentRef); + vMin = Avx2.Min(vMin, v); + } + min = Math.Min(Math.Min(vMin.GetElement(0), vMin.GetElement(1)), Math.Min(vMin.GetElement(2), vMin.GetElement(3))); + } + else if (Avx.IsSupported && values.Length >= 4) + { + Vector256 vMin = Vector256.Create(double.MaxValue); + ref double start = ref MemoryMarshal.GetReference(values); + + for (; i <= values.Length - 4; i += 4) + { + ref double currentRef = ref Unsafe.Add(ref start, i); + var v = Unsafe.As>(ref currentRef); + vMin = Avx.Min(vMin, v); + } + min = Math.Min(Math.Min(vMin.GetElement(0), vMin.GetElement(1)), Math.Min(vMin.GetElement(2), vMin.GetElement(3))); + } + else if (Vector.IsHardwareAccelerated && values.Length >= Vector.Count) + { + int vCount = Vector.Count; + var vMin = new Vector(double.MaxValue); + + for (; i <= values.Length - vCount; i += vCount) + { + var v = new Vector(values.Slice(i, vCount)); + vMin = Vector.Min(vMin, v); + } + + for (int j = 0; j < vCount; j++) + { + if (vMin[j] < min) min = vMin[j]; + } + } + + // Довычисление хвоста + for (; i < values.Length; i++) + { + if (values[i] < min) min = values[i]; + } + + return Unsafe.As(ref min); + } + + public static T Sum(this List list) + where T : struct, IMensuraUnit, IEquatable + { + return CollectionsMarshal.AsSpan(list).Sum(); + } + public static T Average(this List list) + where T : struct, IMensuraUnit, IEquatable + { + return CollectionsMarshal.AsSpan(list).Average(); + } + public static T Max(this List list) + where T : struct, IMensuraUnit, IEquatable + { + return CollectionsMarshal.AsSpan(list).Max(); + } + public static T Min(this List list) + where T : struct, IMensuraUnit, IEquatable + { + return CollectionsMarshal.AsSpan(list).Min(); + } + + public static T Sum(this ICollection collection) + where T : struct, IMensuraUnit, IEquatable + { + if (collection == null || collection.Count == 0) return default; + if (collection is T[] array) return array.Sum(); + if (collection is List list) return list.Sum(); + + T[] sharedArray = ArrayPool.Shared.Rent(collection.Count); + try + { + collection.CopyTo(sharedArray, 0); // Встроенный CopyTo работает быстрее, чем ручной foreach + return new ReadOnlySpan(sharedArray, 0, collection.Count).Sum(); + } + finally + { + ArrayPool.Shared.Return(sharedArray); + } + } + public static T Average(this ICollection collection) + where T : struct, IMensuraUnit, IEquatable + { + if (collection == null || collection.Count == 0) return double.NaN.ToUnit(); + if (collection is T[] array) return array.Average(); + if (collection is List list) return list.Average(); + + T[] sharedArray = ArrayPool.Shared.Rent(collection.Count); + try + { + collection.CopyTo(sharedArray, 0); + return new ReadOnlySpan(sharedArray, 0, collection.Count).Average(); + } + finally + { + ArrayPool.Shared.Return(sharedArray); + } + } + public static T Max(this ICollection collection) + where T : struct, IMensuraUnit, IEquatable + { + if (collection == null || collection.Count == 0) return double.MinValue.ToUnit(); + if (collection is T[] array) return array.Max(); + if (collection is List list) return list.Max(); + + T[] sharedArray = ArrayPool.Shared.Rent(collection.Count); + try + { + collection.CopyTo(sharedArray, 0); + return new ReadOnlySpan(sharedArray, 0, collection.Count).Max(); + } + finally + { + ArrayPool.Shared.Return(sharedArray); + } + } + public static T Min(this ICollection collection) + where T : struct, IMensuraUnit, IEquatable + { + if (collection == null || collection.Count == 0) return double.MaxValue.ToUnit(); + if (collection is T[] array) return array.Min(); + if (collection is List list) return list.Min(); + + T[] sharedArray = ArrayPool.Shared.Rent(collection.Count); + try + { + collection.CopyTo(sharedArray, 0); + return new ReadOnlySpan(sharedArray, 0, collection.Count).Min(); + } + finally + { + ArrayPool.Shared.Return(sharedArray); + } + } + + public static T Sum(this IReadOnlyCollection collection) + where T : struct, IMensuraUnit, IEquatable + { + if (collection == null || collection.Count == 0) return default; + if (collection is T[] array) return array.Sum(); + if (collection is List list) return list.Sum(); + + T[] sharedArray = ArrayPool.Shared.Rent(collection.Count); + int index = 0; + try + { + foreach (var item in collection) sharedArray[index++] = item; + return new ReadOnlySpan(sharedArray, 0, collection.Count).Sum(); + } + finally { ArrayPool.Shared.Return(sharedArray); } + } + public static T Average(this IReadOnlyCollection collection) + where T : struct, IMensuraUnit, IEquatable + { + if (collection == null || collection.Count == 0) return double.NaN.ToUnit(); + if (collection is T[] array) return array.Average(); + if (collection is List list) return list.Average(); + + T[] sharedArray = ArrayPool.Shared.Rent(collection.Count); + int index = 0; + try + { + foreach (var item in collection) sharedArray[index++] = item; + return new ReadOnlySpan(sharedArray, 0, collection.Count).Average(); + } + finally { ArrayPool.Shared.Return(sharedArray); } + } + public static T Max(this IReadOnlyCollection collection) + where T : struct, IMensuraUnit, IEquatable + { + if (collection == null || collection.Count == 0) return double.MinValue.ToUnit(); + if (collection is T[] array) return array.Max(); + if (collection is List list) return list.Max(); + + T[] sharedArray = ArrayPool.Shared.Rent(collection.Count); + int index = 0; + try + { + foreach (var item in collection) sharedArray[index++] = item; + return new ReadOnlySpan(sharedArray, 0, collection.Count).Max(); + } + finally { ArrayPool.Shared.Return(sharedArray); } + } + public static T Min(this IReadOnlyCollection collection) + where T : struct, IMensuraUnit, IEquatable + { + if (collection == null || collection.Count == 0) return double.MaxValue.ToUnit(); + if (collection is T[] array) return array.Min(); + if (collection is List list) return list.Min(); + + T[] sharedArray = ArrayPool.Shared.Rent(collection.Count); + int index = 0; + try + { + foreach (var item in collection) sharedArray[index++] = item; + return new ReadOnlySpan(sharedArray, 0, collection.Count).Min(); + } + finally { ArrayPool.Shared.Return(sharedArray); } + } + + public static T Sum(this IEnumerable collection) + where T : struct, IMensuraUnit, IEquatable + { + if (collection == null) return default; + + // Быстрый SIMD-путь для готовых коллекций (0 аллокаций) + if (collection is T[] array) return array.Sum(); + if (collection is List list) return list.Sum(); + if (collection is ICollection col) return col.Sum(); + if (collection is IReadOnlyCollection roc) return roc.Sum(); + + // Медленный путь для yield return: считаем на лету без буферов + double sum = 0; + bool hasElements = false; + + foreach (var item in collection) + { + sum += item.ToDouble(); + hasElements = true; + } + + return hasElements ? sum.ToUnit() : default; + } + public static T Average(this IEnumerable collection) + where T : struct, IMensuraUnit, IEquatable + { + if (collection == null) return double.NaN.ToUnit(); + + if (collection is T[] array) return array.Average(); + if (collection is List list) return list.Average(); + if (collection is ICollection col) return col.Average(); + if (collection is IReadOnlyCollection roc) return roc.Average(); + + double sum = 0; + long count = 0; + + foreach (var item in collection) + { + sum += item.ToDouble(); + count++; + } + + if (count == 0) return double.NaN.ToUnit(); + + double avg = sum / count; + return avg.ToUnit(); + } + public static T Max(this IEnumerable collection) + where T : struct, IMensuraUnit, IEquatable + { + if (collection == null) return double.MinValue.ToUnit(); + + if (collection is T[] array) return array.Max(); + if (collection is List list) return list.Max(); + if (collection is ICollection col) return col.Max(); + if (collection is IReadOnlyCollection roc) return roc.Max(); + + double max = double.MinValue; + bool hasElements = false; + + foreach (var item in collection) + { + double val = item.ToDouble(); + if (val > max) max = val; + hasElements = true; + } + + return hasElements ? max.ToUnit() : double.MinValue.ToUnit(); + } + public static T Min(this IEnumerable collection) + where T : struct, IMensuraUnit, IEquatable + { + if (collection == null) return double.MaxValue.ToUnit(); + + if (collection is T[] array) return array.Min(); + if (collection is List list) return list.Min(); + if (collection is ICollection col) return col.Min(); + if (collection is IReadOnlyCollection roc) return roc.Min(); + + double min = double.MaxValue; + bool hasElements = false; + + foreach (var item in collection) + { + double val = item.ToDouble(); + if (val < min) min = val; + hasElements = true; + } + + return hasElements ? min.ToUnit() : double.MaxValue.ToUnit(); + } + + // ========================================== + // Sum Average Max Min (nullable) + // ========================================== + + public static T Sum(this ReadOnlySpan units) + where T : struct, IMensuraUnit, IEquatable + { + if (units.IsEmpty) return default; + + // Берем массив из пула строго под размер исходного Span + double[] sharedArray = ArrayPool.Shared.Rent(units.Length); + int validCount = 0; + + try + { + for (int i = 0; i < units.Length; i++) + { + T? unit = units[i]; + if (unit.HasValue) + { + sharedArray[validCount++] = unit.Value.ToDouble(); + } + } + + if (validCount == 0) return default; + + var validValues = new ReadOnlySpan(sharedArray, 0, validCount); + ReadOnlySpan validStructs = MemoryMarshal.Cast(validValues); + + return validStructs.Sum(); // Наш SIMD метод + } + finally + { + ArrayPool.Shared.Return(sharedArray); + } + } + public static T Average(this ReadOnlySpan units) + where T : struct, IMensuraUnit, IEquatable + { + if (units.IsEmpty) + { + double nan = double.NaN; + return Unsafe.As(ref nan); + } + + double[] sharedArray = ArrayPool.Shared.Rent(units.Length); + int validCount = 0; + + try + { + for (int i = 0; i < units.Length; i++) + { + T? unit = units[i]; + if (unit.HasValue) + { + sharedArray[validCount++] = unit.Value.ToDouble(); + } + } + + if (validCount == 0) + { + double nan = double.NaN; + return Unsafe.As(ref nan); + } + + var validValues = new ReadOnlySpan(sharedArray, 0, validCount); + ReadOnlySpan validStructs = MemoryMarshal.Cast(validValues); + + return validStructs.Average(); // Наш SIMD метод + } + finally + { + ArrayPool.Shared.Return(sharedArray); + } + } + public static T Max(this ReadOnlySpan units) + where T : struct, IMensuraUnit, IEquatable + { + if (units.IsEmpty) + { + double minVal = double.MinValue; + return Unsafe.As(ref minVal); + } + + double[] sharedArray = ArrayPool.Shared.Rent(units.Length); + int validCount = 0; + + try + { + for (int i = 0; i < units.Length; i++) + { + T? unit = units[i]; + if (unit.HasValue) + { + sharedArray[validCount++] = unit.Value.ToDouble(); + } + } + + if (validCount == 0) + { + double minVal = double.MinValue; + return Unsafe.As(ref minVal); + } + + var validValues = new ReadOnlySpan(sharedArray, 0, validCount); + ReadOnlySpan validStructs = MemoryMarshal.Cast(validValues); + + return validStructs.Max(); // Наш SIMD метод + } + finally + { + ArrayPool.Shared.Return(sharedArray); + } + } + public static T Min(this ReadOnlySpan units) + where T : struct, IMensuraUnit, IEquatable + { + if (units.IsEmpty) + { + double maxVal = double.MaxValue; + return Unsafe.As(ref maxVal); + } + + double[] sharedArray = ArrayPool.Shared.Rent(units.Length); + int validCount = 0; + + try + { + for (int i = 0; i < units.Length; i++) + { + T? unit = units[i]; + if (unit.HasValue) + { + sharedArray[validCount++] = unit.Value.ToDouble(); + } + } + + if (validCount == 0) + { + double maxVal = double.MaxValue; + return Unsafe.As(ref maxVal); + } + + var validValues = new ReadOnlySpan(sharedArray, 0, validCount); + ReadOnlySpan validStructs = MemoryMarshal.Cast(validValues); + + return validStructs.Min(); // Наш SIMD метод + } + finally + { + ArrayPool.Shared.Return(sharedArray); + } + } + + public static T Sum(this List list) + where T : struct, IMensuraUnit, IEquatable + { + return CollectionsMarshal.AsSpan(list).Sum(); + } + public static T Average(this List list) + where T : struct, IMensuraUnit, IEquatable + { + return CollectionsMarshal.AsSpan(list).Average(); + } + public static T Max(this List list) + where T : struct, IMensuraUnit, IEquatable + { + return CollectionsMarshal.AsSpan(list).Max(); + } + public static T Min(this List list) + where T : struct, IMensuraUnit, IEquatable + { + return CollectionsMarshal.AsSpan(list).Min(); + } + + public static T Sum(this ICollection collection) + where T : struct, IMensuraUnit, IEquatable + { + if (collection == null || collection.Count == 0) return default; + if (collection is T?[] array) return array.Sum(); + if (collection is List list) return list.Sum(); + + T?[] sharedArray = ArrayPool.Shared.Rent(collection.Count); + try + { + collection.CopyTo(sharedArray, 0); + return new ReadOnlySpan(sharedArray, 0, collection.Count).Sum(); + } + finally + { + ArrayPool.Shared.Return(sharedArray); + } + } + public static T Average(this ICollection collection) + where T : struct, IMensuraUnit, IEquatable + { + if (collection == null || collection.Count == 0) return double.NaN.ToUnit(); + if (collection is T?[] array) return array.Average(); + if (collection is List list) return list.Average(); + + T?[] sharedArray = ArrayPool.Shared.Rent(collection.Count); + try + { + collection.CopyTo(sharedArray, 0); + return new ReadOnlySpan(sharedArray, 0, collection.Count).Average(); + } + finally + { + ArrayPool.Shared.Return(sharedArray); + } + } + public static T Max(this ICollection collection) + where T : struct, IMensuraUnit, IEquatable + { + if (collection == null || collection.Count == 0) return double.MinValue.ToUnit(); + if (collection is T?[] array) return array.Max(); + if (collection is List list) return list.Max(); + + T?[] sharedArray = ArrayPool.Shared.Rent(collection.Count); + try + { + collection.CopyTo(sharedArray, 0); + return new ReadOnlySpan(sharedArray, 0, collection.Count).Max(); + } + finally + { + ArrayPool.Shared.Return(sharedArray); + } + } + public static T Min(this ICollection collection) + where T : struct, IMensuraUnit, IEquatable + { + if (collection == null || collection.Count == 0) return double.MaxValue.ToUnit(); + if (collection is T?[] array) return array.Min(); + if (collection is List list) return list.Min(); + + T?[] sharedArray = ArrayPool.Shared.Rent(collection.Count); + try + { + collection.CopyTo(sharedArray, 0); + return new ReadOnlySpan(sharedArray, 0, collection.Count).Min(); + } + finally + { + ArrayPool.Shared.Return(sharedArray); + } + } + + public static T Sum(this IReadOnlyCollection collection) + where T : struct, IMensuraUnit, IEquatable + { + if (collection == null || collection.Count == 0) return default; + if (collection is T?[] array) return array.Sum(); + if (collection is List list) return list.Sum(); + + T?[] sharedArray = ArrayPool.Shared.Rent(collection.Count); + int index = 0; + try + { + foreach (var item in collection) sharedArray[index++] = item; + return new ReadOnlySpan(sharedArray, 0, collection.Count).Sum(); + } + finally { ArrayPool.Shared.Return(sharedArray); } + } + public static T Average(this IReadOnlyCollection collection) + where T : struct, IMensuraUnit, IEquatable + { + if (collection == null || collection.Count == 0) return double.NaN.ToUnit(); + if (collection is T?[] array) return array.Average(); + if (collection is List list) return list.Sum(); // Подхватит Average() из-за правильной перегрузки + + T?[] sharedArray = ArrayPool.Shared.Rent(collection.Count); + int index = 0; + try + { + foreach (var item in collection) sharedArray[index++] = item; + return new ReadOnlySpan(sharedArray, 0, collection.Count).Average(); + } + finally { ArrayPool.Shared.Return(sharedArray); } + } + public static T Max(this IReadOnlyCollection collection) + where T : struct, IMensuraUnit, IEquatable + { + if (collection == null || collection.Count == 0) return double.MinValue.ToUnit(); + if (collection is T?[] array) return array.Max(); + if (collection is List list) return list.Max(); + + T?[] sharedArray = ArrayPool.Shared.Rent(collection.Count); + int index = 0; + try + { + foreach (var item in collection) sharedArray[index++] = item; + return new ReadOnlySpan(sharedArray, 0, collection.Count).Max(); + } + finally { ArrayPool.Shared.Return(sharedArray); } + } + public static T Min(this IReadOnlyCollection collection) + where T : struct, IMensuraUnit, IEquatable + { + if (collection == null || collection.Count == 0) return double.MaxValue.ToUnit(); + if (collection is T?[] array) return array.Min(); + if (collection is List list) return list.Min(); + + T?[] sharedArray = ArrayPool.Shared.Rent(collection.Count); + int index = 0; + try + { + foreach (var item in collection) sharedArray[index++] = item; + return new ReadOnlySpan(sharedArray, 0, collection.Count).Min(); + } + finally { ArrayPool.Shared.Return(sharedArray); } + } + + public static T Sum(this IEnumerable collection) + where T : struct, IMensuraUnit, IEquatable + { + if (collection == null) return default; + + // Быстрый SIMD-путь для готовых коллекций за 0 аллокаций + if (collection is T?[] array) return array.Sum(); + if (collection is List list) return list.Sum(); + if (collection is ICollection col) return col.Sum(); + if (collection is IReadOnlyCollection roc) return roc.Sum(); + + // Медленный путь для ленивого yield return: вычисляем на лету + double sum = 0; + bool hasElements = false; + + foreach (T? item in collection) + { + if (item.HasValue) + { + sum += item.Value.ToDouble(); + hasElements = true; + } + } + + return hasElements ? sum.ToUnit() : default; + } + public static T Average(this IEnumerable collection) + where T : struct, IMensuraUnit, IEquatable + { + if (collection == null) return double.NaN.ToUnit(); + + if (collection is T?[] array) return array.Average(); + if (collection is List list) return list.Average(); + if (collection is ICollection col) return col.Average(); + if (collection is IReadOnlyCollection roc) return roc.Average(); + + double sum = 0; + long count = 0; + + foreach (T? item in collection) + { + if (item.HasValue) + { + sum += item.Value.ToDouble(); + count++; + } + } + + if (count == 0) return double.NaN.ToUnit(); + + double avg = sum / count; + return avg.ToUnit(); + } + public static T Max(this IEnumerable collection) + where T : struct, IMensuraUnit, IEquatable + { + if (collection == null) return double.MinValue.ToUnit(); + + if (collection is T?[] array) return array.Max(); + if (collection is List list) return list.Max(); + if (collection is ICollection col) return col.Max(); + if (collection is IReadOnlyCollection roc) return roc.Max(); + + double max = double.MinValue; + bool hasElements = false; + + foreach (T? item in collection) + { + if (item.HasValue) + { + double val = item.Value.ToDouble(); + if (val > max) max = val; + hasElements = true; + } + } + + return hasElements ? max.ToUnit() : double.MinValue.ToUnit(); + } + public static T Min(this IEnumerable collection) + where T : struct, IMensuraUnit, IEquatable + { + if (collection == null) return double.MaxValue.ToUnit(); + + if (collection is T?[] array) return array.Min(); + if (collection is List list) return list.Min(); + if (collection is ICollection col) return col.Min(); + if (collection is IReadOnlyCollection roc) return roc.Min(); + + double min = double.MaxValue; + bool hasElements = false; + + foreach (T? item in collection) + { + if (item.HasValue) + { + double val = item.Value.ToDouble(); + if (val < min) min = val; + hasElements = true; + } + } + + return hasElements ? min.ToUnit() : double.MaxValue.ToUnit(); + } + + // ========================================== + + + + // ========================================== + // CORE + // ========================================== + internal static void MultiplyCore(ReadOnlySpan source, double multiplicator, Span destination) + where T : struct, IMensuraUnit, IEquatable + { + if (source.IsEmpty) return; + + int len = source.Length; + ReadOnlySpan srcDouble = MemoryMarshal.Cast(source); + Span dstDouble = MemoryMarshal.Cast(destination); + + var vectorizedMultiplicator = new Vector(multiplicator); + int vectorSize = Vector.Count; + int i = 0; + + ref double srcRef = ref MemoryMarshal.GetReference(srcDouble); + ref double dstRef = ref MemoryMarshal.GetReference(dstDouble); + + int simdEnd = len & ~(vectorSize - 1); + + for (; i < simdEnd; i += vectorSize) + { + ref double currentSrc = ref Unsafe.Add(ref srcRef, i); + ref double currentDst = ref Unsafe.Add(ref dstRef, i); + + var vector = Unsafe.As>(ref currentSrc); + var multiplied = vector * vectorizedMultiplicator; + + Unsafe.As>(ref currentDst) = multiplied; + } + + for (; i < len; i++) + { + Unsafe.Add(ref dstRef, i) = Unsafe.Add(ref srcRef, i) * multiplicator; + } + } + internal static void DivideCore(ReadOnlySpan dividends, double divisor, Span destination) + where T : struct, IMensuraUnit, IEquatable + { + if (dividends.IsEmpty) return; + + int len = dividends.Length; + ReadOnlySpan srcDouble = MemoryMarshal.Cast(dividends); + Span dstDouble = MemoryMarshal.Cast(destination); + + // Вместо деления в цикле, умножаем на обратное число (invDivisor) + double invDivisor = 1.0 / divisor; + var vectorizedInvDivisor = new Vector(invDivisor); + + int vectorSize = Vector.Count; + int i = 0; + + ref double srcRef = ref MemoryMarshal.GetReference(srcDouble); + ref double dstRef = ref MemoryMarshal.GetReference(dstDouble); + + int simdEnd = len & ~(vectorSize - 1); + + for (; i < simdEnd; i += vectorSize) + { + ref double currentSrc = ref Unsafe.Add(ref srcRef, i); + ref double currentDst = ref Unsafe.Add(ref dstRef, i); + + // Загрузка, быстрое умножение вместо деления, и сохранение + var vector = Unsafe.As>(ref currentSrc); + var multiplied = vector * vectorizedInvDivisor; + + Unsafe.As>(ref currentDst) = multiplied; + } + + // Хвост + for (; i < len; i++) + { + Unsafe.Add(ref dstRef, i) = Unsafe.Add(ref srcRef, i) * invDivisor; + } + } + internal static void DivideCore(double dividend, ReadOnlySpan divisors, Span destination) + where T : struct, IMensuraUnit, IEquatable + { + if (divisors.IsEmpty) return; + + int len = divisors.Length; + ReadOnlySpan srcDouble = MemoryMarshal.Cast(divisors); + Span dstDouble = MemoryMarshal.Cast(destination); + + var vectorizedDividend = new Vector(dividend); + int vectorSize = Vector.Count; + int i = 0; + + ref double srcRef = ref MemoryMarshal.GetReference(srcDouble); + ref double dstRef = ref MemoryMarshal.GetReference(dstDouble); + + int simdEnd = len & ~(vectorSize - 1); + + for (; i < simdEnd; i += vectorSize) + { + ref double currentSrc = ref Unsafe.Add(ref srcRef, i); + ref double currentDst = ref Unsafe.Add(ref dstRef, i); + + var vector = Unsafe.As>(ref currentSrc); + + // Векторное деление: константа делится на покомпонентные элементы массива + var divided = Vector.Divide(vectorizedDividend, vector); + + Unsafe.As>(ref currentDst) = divided; + } + + // Хвост + for (; i < len; i++) + { + Unsafe.Add(ref dstRef, i) = dividend / Unsafe.Add(ref srcRef, i); + } + } + internal static void PlusCore(ReadOnlySpan summands, double summand, Span destination) + where T : struct, IMensuraUnit, IEquatable + { + if (summands.IsEmpty) return; + + int len = summands.Length; + ReadOnlySpan srcDouble = MemoryMarshal.Cast(summands); + Span dstDouble = MemoryMarshal.Cast(destination); + + var vectorizedSummand = 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 & ~(vectorSize - 1); + + for (; i < simdEnd; i += vectorSize) + { + ref double currentSrc = ref Unsafe.Add(ref srcRef, i); + ref double currentDst = ref Unsafe.Add(ref dstRef, i); + + var vector = Unsafe.As>(ref currentSrc); + var added = vector + vectorizedSummand; + + Unsafe.As>(ref currentDst) = added; + } + + // Хвост + for (; i < len; i++) + { + Unsafe.Add(ref dstRef, i) = Unsafe.Add(ref srcRef, i) + summand; + } + } + internal static void MinusCore(ReadOnlySpan minuends, double subtrahend, Span destination) + where T : struct, IMensuraUnit, IEquatable + { + if (minuends.IsEmpty) return; + + int len = minuends.Length; + ReadOnlySpan srcDouble = MemoryMarshal.Cast(minuends); + Span dstDouble = MemoryMarshal.Cast(destination); + + var vectorizedSubtrahend = 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 & ~(vectorSize - 1); + + for (; i < simdEnd; i += vectorSize) + { + ref double currentSrc = ref Unsafe.Add(ref srcRef, i); + ref double currentDst = ref Unsafe.Add(ref dstRef, i); + + var vector = Unsafe.As>(ref currentSrc); + var subtracted = vector - vectorizedSubtrahend; + + Unsafe.As>(ref currentDst) = subtracted; + } + + // Хвост + for (; i < len; i++) + { + Unsafe.Add(ref dstRef, i) = Unsafe.Add(ref srcRef, i) - subtrahend; + } + } + internal static void MinusCore(double minuend, ReadOnlySpan subtrahends, Span destination) + where T : struct, IMensuraUnit, IEquatable + { + if (subtrahends.IsEmpty) return; + + int len = subtrahends.Length; + ReadOnlySpan srcDouble = MemoryMarshal.Cast(subtrahends); + Span dstDouble = MemoryMarshal.Cast(destination); + + var vectorizedMinuend = 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 & ~(vectorSize - 1); + + for (; i < simdEnd; i += vectorSize) + { + ref double currentSrc = ref Unsafe.Add(ref srcRef, i); + ref double currentDst = ref Unsafe.Add(ref dstRef, i); + + var vector = Unsafe.As>(ref currentSrc); + + // Векторное вычитание: константа уменьшается на покомпонентные элементы массива + var subtracted = vectorizedMinuend - vector; + + Unsafe.As>(ref currentDst) = subtracted; + } + + // Хвост + for (; i < len; i++) + { + Unsafe.Add(ref dstRef, i) = minuend - Unsafe.Add(ref srcRef, i); + } + } + + internal static void PowCore(ReadOnlySpan source, int power, Span destination) + where T : struct, IMensuraUnit, IEquatable + { + if (source.IsEmpty) return; + + int len = source.Length; + ReadOnlySpan srcDouble = MemoryMarshal.Cast(source); + Span dstDouble = MemoryMarshal.Cast(destination); + + int vectorSize = Vector.Count; + int i = 0; + + ref double srcRef = ref MemoryMarshal.GetReference(srcDouble); + ref double dstRef = ref MemoryMarshal.GetReference(dstDouble); + + // Быстрая обработка граничных случаев для исключения лишних циклов + if (power == 0) + { + // Любое число в степени 0 равно 1 + dstDouble.Fill(1.0); + return; + } + if (power == 1) + { + // В степени 1 — это просто копирование памяти + srcDouble.CopyTo(dstDouble); + return; + } + + int simdEnd = len & ~(vectorSize - 1); + + for (; i < simdEnd; i += vectorSize) + { + ref double currentSrc = ref Unsafe.Add(ref srcRef, i); + ref double currentDst = ref Unsafe.Add(ref dstRef, i); + + // Загружаем вектор за 1 такт процессора + var @base = Unsafe.As>(ref currentSrc); + + // Векторный аналог быстрого (бинарного) возведения в степень + var result = Vector.One; + var currentPower = Math.Abs(power); + + while (currentPower > 0) + { + if ((currentPower & 1) == 1) + { + result *= @base; + } + @base *= @base; + currentPower >>= 1; + } + + // Если степень отрицательная, делим единицу на полученный результат + if (power < 0) + { + result = Vector.Divide(Vector.One, result); + } + + // Сохраняем итоговый вектор + Unsafe.As>(ref currentDst) = result; + } + + // Хвост массива (или обычный расчет, если SIMD не поддерживается) + var absPower = Math.Abs(power); + for (; i < len; i++) + { + double @base = Unsafe.Add(ref srcRef, i); + double result = 1.0; + int currentPower = absPower; + + while (currentPower > 0) + { + if ((currentPower & 1) == 1) result *= @base; + @base *= @base; + currentPower >>= 1; + } + + Unsafe.Add(ref dstRef, i) = power < 0 ? 1.0 / result : result; + } + } + internal static void PowCore(ReadOnlySpan source, double power, Span destination) + where T : struct, IMensuraUnit, IEquatable + { + if (source.IsEmpty) return; + + int len = source.Length; + ReadOnlySpan srcDouble = MemoryMarshal.Cast(source); + Span dstDouble = MemoryMarshal.Cast(destination); + + int i = 0; + + ref double srcRef = ref MemoryMarshal.GetReference(srcDouble); + ref double dstRef = ref MemoryMarshal.GetReference(dstDouble); + + // 1. Оптимизация тривиальных случаев + if (power == 1.0) + { + srcDouble.CopyTo(dstDouble); + return; + } + if (power == 0.0) + { + dstDouble.Fill(1.0); + return; + } + + // 2. Оптимизация для квадратного корня (степень 0.5) через аппаратный AVX + if (power == 0.5) + { + if (Avx.IsSupported && len >= 4) + { + int simdEnd = len & ~3; + for (; i < simdEnd; i += 4) + { + // Совместимый с .NET 6 способ чтения вектора из ref double + ref double currentSrc = ref Unsafe.Add(ref srcRef, i); + var v = Unsafe.As>(ref currentSrc); + + // Аппаратный корень через инструкцию vsqrtpd + var sqrtV = Avx.Sqrt(v); + + // Совместимый с .NET 6 способ записи вектора в ref double + ref double currentDst = ref Unsafe.Add(ref dstRef, i); + Unsafe.As>(ref currentDst) = sqrtV; + } + } + + // Хвост для корня + for (; i < len; i++) + { + Unsafe.Add(ref dstRef, i) = Math.Sqrt(Unsafe.Add(ref srcRef, i)); + } + return; + } + + // 3. ОБЩИЙ СЛУЧАЙ ДЛЯ СЛОЖНЫХ СТЕПЕНЕЙ + // Из-за ограничений .NET 6 здесь мы разворачиваем цикл вручную на 4 элемента, + // но делаем это через ref-ссылки без использования fixed-указателей + if (len >= 4) + { + int simdEnd = len & ~3; + for (; i < simdEnd; i += 4) + { + ref double sRef = ref Unsafe.Add(ref srcRef, i); + ref double dRef = ref Unsafe.Add(ref dstRef, i); + + // JIT попытается это автовекторизовать, но даже без нее + // этот код работает быстрее fixed за счет отсутствия пиннинга памяти GC + Unsafe.Add(ref dRef, 0) = Math.Pow(Unsafe.Add(ref sRef, 0), power); + Unsafe.Add(ref dRef, 1) = Math.Pow(Unsafe.Add(ref sRef, 1), power); + Unsafe.Add(ref dRef, 2) = Math.Pow(Unsafe.Add(ref sRef, 2), power); + Unsafe.Add(ref dRef, 3) = Math.Pow(Unsafe.Add(ref sRef, 3), power); + } + } + + // Хвост массива + for (; i < len; i++) + { + Unsafe.Add(ref dstRef, i) = Math.Pow(Unsafe.Add(ref srcRef, i), power); + } + } + internal static void SqrtCore(ReadOnlySpan source, Span destination) + where T : struct, IMensuraUnit, IEquatable + { + if (source.IsEmpty) return; + + int len = source.Length; + ReadOnlySpan srcDouble = MemoryMarshal.Cast(source); + Span dstDouble = MemoryMarshal.Cast(destination); + + int i = 0; + + ref double srcRef = ref MemoryMarshal.GetReference(srcDouble); + ref double dstRef = ref MemoryMarshal.GetReference(dstDouble); + + // 1. ПУТЬ AVX (x64 Процессоры Intel/AMD) — обрабатываем по 4 элемента double + if (Avx.IsSupported && len >= 4) + { + int simdEnd = len & ~3; // Вычисляем границу по маске (быстрее, чем len - len % 4) + + for (; i < simdEnd; i += 4) + { + ref double currentSrc = ref Unsafe.Add(ref srcRef, i); + ref double currentDst = ref Unsafe.Add(ref dstRef, i); + + // Загружаем 4 элемента double в AVX регистр за 1 такт + var v = Unsafe.As>(ref currentSrc); + + // Аппаратный корень на уровне CPU + var sqrtV = Avx.Sqrt(v); + + // Выгружаем результат обратно в память назначения за 1 такт + Unsafe.As>(ref currentDst) = sqrtV; + } + } + // 2. ПУТЬ VECTOR (ARM64 / Apple Silicon / Старые CPU без AVX) + else if (Vector.IsHardwareAccelerated && len >= Vector.Count) + { + int vCount = Vector.Count; + int simdEnd = len & ~(vCount - 1); + + for (; i < simdEnd; i += vCount) + { + // Используем Span для кроссплатформенного создания вектора + var v = new Vector(srcDouble.Slice(i, vCount)); + + // Кроссплатформенный аппаратный корень (на ARM превратится в NEON инструкцию) + var sqrtV = Vector.SquareRoot(v); + + // Копируем напрямую в целевой Span + sqrtV.CopyTo(dstDouble.Slice(i, vCount)); + } + } + + // 3. Хвост массива (или обычный расчет, если SIMD на процессоре недоступен) + for (; i < len; i++) + { + Unsafe.Add(ref dstRef, i) = Math.Sqrt(Unsafe.Add(ref srcRef, i)); + } + } + + + + + // ========================================== + // === MULTIPLY === + // ========================================== + + public static T[] Multiply(this T[] units, double multiplicator) + where T : struct, IMensuraUnit, IEquatable + { + if (units is null) return null!; + int len = units.Length; + if (len == 0) return []; + + var result = new T[len]; + MultiplyCore(units, multiplicator, result); + return result; + } + public static T?[] Multiply(this T?[] units, double multiplicator) + where T : struct, IMensuraUnit, IEquatable + { + if (units is null) return null!; + int len = units.Length; + if (len == 0) return []; + + // Выделяем чистую память (0 аллокаций логики, только сам массив) + var result = new T?[len]; + + for (int i = 0; i < len; i++) + { + // Читаем из исходного по значению (бесплатно для 8 байт) + T? item = units[i]; + if (item.HasValue) + { + // Пишем напрямую в результат по ref-ссылке (всего 1 запись в память!) + ref var dst = ref result[i]; + dst = (item.Value.ToDouble() * multiplicator).ToUnit(); + } + } + return result; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T[] Multiply(this double multiplicator, T[] units) + where T : struct, IMensuraUnit, IEquatable => units.Multiply(multiplicator); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T?[] Multiply(this double multiplicator, T?[] units) + where T : struct, IMensuraUnit, IEquatable => units.Multiply(multiplicator); + + // === ReadOnlySpan === + public static void Multiply(this ReadOnlySpan units, double multiplicator, Span destination) + where T : struct, IMensuraUnit, IEquatable + { + if (units.Length > destination.Length) + throw new ArgumentException("Целевой буфер destination меньше исходного source."); + Extensions.MultiplyCore(units, multiplicator, destination); + } + public static void Multiply(this ReadOnlySpan units, double multiplicator, Span destination) + where T : struct, IMensuraUnit, IEquatable + { + if (units.IsEmpty) return; + + int len = units.Length; + + // Получаем прямые неуправляемые ref-ссылки на начало буферов за 0 тактов + ref var srcRef = ref MemoryMarshal.GetReference(units); + ref var dstRef = ref MemoryMarshal.GetReference(destination); + + int i = 0; + int unrollEnd = len & ~3; // Граница развернутого цикла (кратная 4) + + // 1. ОСНОВНОЙ ЦИКЛ: Обрабатываем конвейером по 4 элемента за итерацию + for (; i < unrollEnd; i += 4) + { + T? u0 = Unsafe.Add(ref srcRef, i); + T? u1 = Unsafe.Add(ref srcRef, i + 1); + T? u2 = Unsafe.Add(ref srcRef, i + 2); + T? u3 = Unsafe.Add(ref srcRef, i + 3); + + // Получаем ref-ссылки на ячейки назначения (zero-cost адресация) + ref var d0 = ref Unsafe.Add(ref dstRef, i); + ref var d1 = ref Unsafe.Add(ref dstRef, i + 1); + ref var d2 = ref Unsafe.Add(ref dstRef, i + 2); + ref var d3 = ref Unsafe.Add(ref dstRef, i + 3); + + // Пишем строго по месту. Если HasValue == false, в ячейку dX запишется null (сбросятся байты флага) + d0 = u0.HasValue ? (u0.Value.ToDouble() * multiplicator).ToUnit() : null; + d1 = u1.HasValue ? (u1.Value.ToDouble() * multiplicator).ToUnit() : null; + d2 = u2.HasValue ? (u2.Value.ToDouble() * multiplicator).ToUnit() : null; + d3 = u3.HasValue ? (u3.Value.ToDouble() * multiplicator).ToUnit() : null; + } + + // 2. ХВОСТ ЦИКЛА: Довычисляем остаток элементов (от 1 до 3 штук) + for (; i < len; i++) + { + T? unit = Unsafe.Add(ref srcRef, i); + ref var dst = ref Unsafe.Add(ref dstRef, i); + + dst = unit.HasValue ? (unit.Value.ToDouble() * multiplicator).ToUnit() : null; + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Multiply(this double multiplicator, ReadOnlySpan units, Span destination) + where T : struct, IMensuraUnit, IEquatable => units.Multiply(multiplicator, destination); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Multiply(this double multiplicator, ReadOnlySpan units, Span destination) + where T : struct, IMensuraUnit, IEquatable => units.Multiply(multiplicator, destination); + + // === List === + public static List Multiply(this List units, double multiplicator) + where T : struct, IMensuraUnit, IEquatable + { + if (units is null) return null!; + int len = units.Count; + if (len == 0) return []; + + var result = new List(len); + result.SetCountUnsafe(len); + Extensions.MultiplyCore(CollectionsMarshal.AsSpan(units), multiplicator, CollectionsMarshal.AsSpan(result)); + return result; + } + public static List Multiply(this List units, double multiplicator) + where T : struct, IMensuraUnit, IEquatable + { + if (units is null) return null!; + int count = units.Count; + if (count == 0) return []; + + var resultArray = new T?[count]; + ReadOnlySpan srcSpan = CollectionsMarshal.AsSpan(units); + for (int i = 0; i < count; i++) + { + T? item = srcSpan[i]; + if (item.HasValue) + { + ref var dst = ref resultArray[i]; + dst = (item.Value.ToDouble() * multiplicator).ToUnit(); + } + } + return [.. resultArray]; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static List Multiply(this double multiplicator, List units) + where T : struct, IMensuraUnit, IEquatable => units.Multiply(multiplicator); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static List Multiply(this double multiplicator, List units) + where T : struct, IMensuraUnit, IEquatable => units.Multiply(multiplicator); + + // === ICollection === + public static ICollection Multiply(this ICollection units, double multiplicator) + where T : struct, IMensuraUnit, IEquatable + { + if (units is null) return null!; + int count = units.Count; + if (count == 0) return []; + if (units is T[] array) return array.Multiply(multiplicator); + if (units is List list) return list.Multiply(multiplicator); + + T[] sharedArray = ArrayPool.Shared.Rent(count); + var finalResult = new T[count]; + try + { + units.CopyTo(sharedArray, 0); + MultiplyCore(new ReadOnlySpan(sharedArray, 0, count), multiplicator, finalResult.AsSpan()); + return finalResult; + } + finally + { + ArrayPool.Shared.Return(sharedArray); + } + } + public static ICollection Multiply(this ICollection units, double multiplicator) + where T : struct, IMensuraUnit, IEquatable + { + if (units is null) return null!; + int count = units.Count; + if (count == 0) return []; + if (units is T?[] array) return array.Multiply(multiplicator); + if (units is List list) return list.Multiply(multiplicator); + + var resultArray = new T?[count]; + T?[] sharedNullableArray = ArrayPool.Shared.Rent(count); + try + { + units.CopyTo(sharedNullableArray, 0); + var srcSpan = new ReadOnlySpan(sharedNullableArray, 0, count); + srcSpan.Multiply(multiplicator, resultArray.AsSpan()); + return resultArray; + } + finally + { + ArrayPool.Shared.Return(sharedNullableArray); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ICollection Multiply(this double multiplicator, ICollection units) + where T : struct, IMensuraUnit, IEquatable => units.Multiply(multiplicator); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ICollection Multiply(this double multiplicator, ICollection units) + where T : struct, IMensuraUnit, IEquatable => units.Multiply(multiplicator); + + // === IReadOnlyCollection === + public static IReadOnlyCollection Multiply(this IReadOnlyCollection units, double multiplicator) + where T : struct, IMensuraUnit, IEquatable + { + if (units is null) return null!; + int count = units.Count; + if (count == 0) return []; + if (units is T[] array) return array.Multiply(multiplicator); + if (units is List list) return list.Multiply(multiplicator); + + T[] sharedArray = ArrayPool.Shared.Rent(count); + var finalResult = new T[count]; + try + { + units.CopyTo(sharedArray, 0); + MultiplyCore(new ReadOnlySpan(sharedArray, 0, count), multiplicator, finalResult.AsSpan()); + return finalResult; + } + finally + { + ArrayPool.Shared.Return(sharedArray); + } + } + public static IReadOnlyCollection Multiply(this IReadOnlyCollection units, double multiplicator) + where T : struct, IMensuraUnit, IEquatable + { + if (units is null) return null!; + int count = units.Count; + if (count == 0) return []; + if (units is T?[] array) return array.Multiply(multiplicator); + if (units is List list) return list.Multiply(multiplicator); + + var resultArray = new T?[count]; + T?[] sharedNullableArray = ArrayPool.Shared.Rent(count); + try + { + units.CopyTo(sharedNullableArray, 0); + var srcSpan = new ReadOnlySpan(sharedNullableArray, 0, count); + srcSpan.Multiply(multiplicator, resultArray.AsSpan()); + return resultArray; + } + finally + { + ArrayPool.Shared.Return(sharedNullableArray); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static IReadOnlyCollection Multiply(this double multiplicator, IReadOnlyCollection units) + where T : struct, IMensuraUnit, IEquatable => units.Multiply(multiplicator); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static IReadOnlyCollection Multiply(this double multiplicator, IReadOnlyCollection units) + where T : struct, IMensuraUnit, IEquatable => units.Multiply(multiplicator); + + // === IEnumerable + yeild === + static IEnumerable MultiplyIterator(IEnumerable units, double multiplicator) + where T : struct, IMensuraUnit, IEquatable + { + foreach (var item in units) + yield return (item.ToDouble() * multiplicator).ToUnit(); + } + static IEnumerable MultiplyNullableIterator(IEnumerable units, double multiplicator) + where T : struct, IMensuraUnit, IEquatable + { + foreach (T? item in units) + yield return item.HasValue + ? (item.Value.ToDouble() * multiplicator).ToUnit() : null; + } + + // === IEnumerable === + public static IEnumerable Multiply(this IEnumerable units, double multiplicator) + where T : struct, IMensuraUnit, IEquatable + { + if (units is null) return null!; + if (units is T[] array) return array.Multiply(multiplicator); + if (units is List list) return list.Multiply(multiplicator); + if (units is ICollection col) return col.Multiply(multiplicator); + if (units is IReadOnlyCollection roc) return roc.Multiply(multiplicator); + return MultiplyIterator(units, multiplicator); + } + public static IEnumerable Multiply(this IEnumerable units, double multiplicator) + where T : struct, IMensuraUnit, IEquatable + { + if (units is null) return null!; + if (units is T?[] array) return array.Multiply(multiplicator); + if (units is List list) return list.Multiply(multiplicator); + if (units is ICollection col) return col.Multiply(multiplicator); + if (units is IReadOnlyCollection roc) return roc.Multiply(multiplicator); + return MultiplyNullableIterator(units, multiplicator); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static IEnumerable Multiply(this double multiplicator, IEnumerable units) + where T : struct, IMensuraUnit, IEquatable => Multiply(units, multiplicator); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static IEnumerable Multiply(this double multiplicator, IEnumerable units) + where T : struct, IMensuraUnit, IEquatable => Multiply(units, multiplicator); + + + + + // ========================================== + // === DIVIDE === + // ========================================== + + public static T[] Divide(this T[] units, double divisor) + where T : struct, IMensuraUnit, IEquatable + { + if (units is null) return null!; + int len = units.Length; + if (len == 0) return []; + + var result = new T[len]; + DivideCore(units, divisor, result); + return result; + } + public static T?[] Divide(this T?[] units, double divisor) + where T : struct, IMensuraUnit, IEquatable + { + if (units is null) return null!; + int len = units.Length; + if (len == 0) return []; + + // Выделяем чистую память (0 аллокаций логики, только сам массив) + var result = new T?[len]; + double invDivisor = 1.0 / divisor; + + for (int i = 0; i < len; i++) + { + // Читаем из исходного по значению (бесплатно для 8 байт) + T? item = units[i]; + if (item.HasValue) + { + // Пишем напрямую в результат по ref-ссылке (всего 1 запись в память!) + ref var dst = ref result[i]; + dst = (item.Value.ToDouble() / invDivisor).ToUnit(); + } + } + return result; + } + public static T[] Divide(this double dividend, T[] units) + where T : struct, IMensuraUnit, IEquatable + { + if (units is null) return null!; + int len = units.Length; + if (len == 0) return []; + + var result = new T[len]; + DivideCore(dividend, units, result); + return result; + } + public static T?[] Divide(this double dividend, T?[] units) + where T : struct, IMensuraUnit, IEquatable + { + if (units is null) return null!; + int len = units.Length; + if (len == 0) return []; + + var result = new T?[len]; + + for (int i = 0; i < len; i++) + { + T? item = units[i]; + if (item.HasValue) + { + ref var dst = ref result[i]; + dst = (dividend / item.Value.ToDouble()).ToUnit(); + } + } + return result; + } + + // === ReadOnlySpan === + public static void Divide(this ReadOnlySpan units, double divisor, Span destination) + where T : struct, IMensuraUnit, IEquatable + { + if (units.Length > destination.Length) + throw new ArgumentException("Целевой буфер destination меньше исходного source."); + Extensions.DivideCore(units, divisor, destination); + } + public static void Divide(this ReadOnlySpan units, double divisor, Span destination) + where T : struct, IMensuraUnit, IEquatable + { + if (units.IsEmpty) return; + + int len = units.Length; + + // Получаем прямые неуправляемые ref-ссылки на начало буферов за 0 тактов + ref var srcRef = ref MemoryMarshal.GetReference(units); + ref var dstRef = ref MemoryMarshal.GetReference(destination); + + int i = 0; + int unrollEnd = len & ~3; // Граница развернутого цикла (кратная 4) + double invDivisor = 1.0 / divisor; + + // 1. ОСНОВНОЙ ЦИКЛ: Обрабатываем конвейером по 4 элемента за итерацию + for (; i < unrollEnd; i += 4) + { + T? u0 = Unsafe.Add(ref srcRef, i); + T? u1 = Unsafe.Add(ref srcRef, i + 1); + T? u2 = Unsafe.Add(ref srcRef, i + 2); + T? u3 = Unsafe.Add(ref srcRef, i + 3); + + // Получаем ref-ссылки на ячейки назначения (zero-cost адресация) + ref var d0 = ref Unsafe.Add(ref dstRef, i); + ref var d1 = ref Unsafe.Add(ref dstRef, i + 1); + ref var d2 = ref Unsafe.Add(ref dstRef, i + 2); + ref var d3 = ref Unsafe.Add(ref dstRef, i + 3); + + // Пишем строго по месту. Если HasValue == false, в ячейку dX запишется null (сбросятся байты флага) + d0 = u0.HasValue ? (u0.Value.ToDouble() / invDivisor).ToUnit() : null; + d1 = u1.HasValue ? (u1.Value.ToDouble() / invDivisor).ToUnit() : null; + d2 = u2.HasValue ? (u2.Value.ToDouble() / invDivisor).ToUnit() : null; + d3 = u3.HasValue ? (u3.Value.ToDouble() / invDivisor).ToUnit() : null; + } + + // 2. ХВОСТ ЦИКЛА: Довычисляем остаток элементов (от 1 до 3 штук) + for (; i < len; i++) + { + T? unit = Unsafe.Add(ref srcRef, i); + ref var dst = ref Unsafe.Add(ref dstRef, i); + + dst = unit.HasValue ? (unit.Value.ToDouble() / invDivisor).ToUnit() : null; + } + } + public static void Divide(this double dividend, ReadOnlySpan units, Span destination) + where T : struct, IMensuraUnit, IEquatable + { + if (units.Length > destination.Length) + throw new ArgumentException("Целевой буфер destination меньше исходного source."); + Extensions.DivideCore(dividend, units, destination); + } + public static void Divide(this double dividend, ReadOnlySpan units, Span destination) + where T : struct, IMensuraUnit, IEquatable + { + if (units.IsEmpty) return; + + int len = units.Length; + + // Получаем прямые неуправляемые ref-ссылки на начало буферов за 0 тактов + ref var srcRef = ref MemoryMarshal.GetReference(units); + ref var dstRef = ref MemoryMarshal.GetReference(destination); + + int i = 0; + int unrollEnd = len & ~3; // Граница развернутого цикла (кратная 4) + + // 1. ОСНОВНОЙ ЦИКЛ: Обрабатываем конвейером по 4 элемента за итерацию + for (; i < unrollEnd; i += 4) + { + T? u0 = Unsafe.Add(ref srcRef, i); + T? u1 = Unsafe.Add(ref srcRef, i + 1); + T? u2 = Unsafe.Add(ref srcRef, i + 2); + T? u3 = Unsafe.Add(ref srcRef, i + 3); + + // Получаем ref-ссылки на ячейки назначения (zero-cost адресация) + ref var d0 = ref Unsafe.Add(ref dstRef, i); + ref var d1 = ref Unsafe.Add(ref dstRef, i + 1); + ref var d2 = ref Unsafe.Add(ref dstRef, i + 2); + ref var d3 = ref Unsafe.Add(ref dstRef, i + 3); + + // Пишем строго по месту. Если HasValue == false, в ячейку dX запишется null (сбросятся байты флага) + d0 = u0.HasValue ? (dividend / u0.Value.ToDouble()).ToUnit() : null; + d1 = u1.HasValue ? (dividend / u1.Value.ToDouble()).ToUnit() : null; + d2 = u2.HasValue ? (dividend / u2.Value.ToDouble()).ToUnit() : null; + d3 = u3.HasValue ? (dividend / u3.Value.ToDouble()).ToUnit() : null; + } + + // 2. ХВОСТ ЦИКЛА: Довычисляем остаток элементов (от 1 до 3 штук) + for (; i < len; i++) + { + T? unit = Unsafe.Add(ref srcRef, i); + ref var dst = ref Unsafe.Add(ref dstRef, i); + + dst = unit.HasValue ? (dividend / unit.Value.ToDouble()).ToUnit() : null; + } + } + + // === List === + public static List Divide(this List units, double divisor) + where T : struct, IMensuraUnit, IEquatable + { + if (units is null) return null!; + int len = units.Count; + if (len == 0) return []; + + var result = new List(len); + result.SetCountUnsafe(len); + Extensions.DivideCore(CollectionsMarshal.AsSpan(units), divisor, CollectionsMarshal.AsSpan(result)); + return result; + } + public static List Divide(this List units, double divisor) + where T : struct, IMensuraUnit, IEquatable + { + if (units is null) return null!; + int count = units.Count; + if (count == 0) return []; + + var resultArray = new T?[count]; + double invDivisor = 1.0 / divisor; + ReadOnlySpan srcSpan = CollectionsMarshal.AsSpan(units); + for (int i = 0; i < count; i++) + { + T? item = srcSpan[i]; + if (item.HasValue) + { + ref var dst = ref resultArray[i]; + dst = (item.Value.ToDouble() / invDivisor).ToUnit(); + } + } + return [.. resultArray]; + } + public static List Divide(this double dividend, List units) + where T : struct, IMensuraUnit, IEquatable + { + if (units is null) return null!; + int len = units.Count; + if (len == 0) return []; + + var result = new List(len); + result.SetCountUnsafe(len); + Extensions.DivideCore(dividend, CollectionsMarshal.AsSpan(units), CollectionsMarshal.AsSpan(result)); + return result; + } + public static List Divide(this double dividend, List units) + where T : struct, IMensuraUnit, IEquatable + { + if (units is null) return null!; + int count = units.Count; + if (count == 0) return []; + + var resultArray = new T?[count]; + ReadOnlySpan srcSpan = CollectionsMarshal.AsSpan(units); + for (int i = 0; i < count; i++) + { + T? item = srcSpan[i]; + if (item.HasValue) + { + ref var dst = ref resultArray[i]; + dst = (dividend / item.Value.ToDouble()).ToUnit(); + } + } + return [.. resultArray]; + } + + // === ICollection === + public static ICollection Divide(this ICollection units, double divisor) + where T : struct, IMensuraUnit, IEquatable + { + if (units is null) return null!; + int count = units.Count; + if (count == 0) return []; + if (units is T[] array) return array.Divide(divisor); + if (units is List list) return list.Divide(divisor); + + T[] sharedArray = ArrayPool.Shared.Rent(count); + var finalResult = new T[count]; + try + { + units.CopyTo(sharedArray, 0); + DivideCore(new ReadOnlySpan(sharedArray, 0, count), divisor, finalResult.AsSpan()); + return finalResult; + } + finally + { + ArrayPool.Shared.Return(sharedArray); + } + } + public static ICollection Divide(this ICollection units, double divisor) + where T : struct, IMensuraUnit, IEquatable + { + if (units is null) return null!; + int count = units.Count; + if (count == 0) return []; + if (units is T?[] array) return array.Divide(divisor); + if (units is List list) return list.Divide(divisor); + + var resultArray = new T?[count]; + T?[] sharedNullableArray = ArrayPool.Shared.Rent(count); + try + { + units.CopyTo(sharedNullableArray, 0); + var srcSpan = new ReadOnlySpan(sharedNullableArray, 0, count); + srcSpan.Divide(divisor, resultArray.AsSpan()); + return resultArray; + } + finally + { + ArrayPool.Shared.Return(sharedNullableArray); + } + } + public static ICollection Divide(this double dividend, ICollection units) + where T : struct, IMensuraUnit, IEquatable + { + if (units is null) return null!; + int count = units.Count; + if (count == 0) return []; + if (units is T[] array) return dividend.Divide(array); + if (units is List list) return dividend.Divide(list); + + T[] sharedArray = ArrayPool.Shared.Rent(count); + var finalResult = new T[count]; + try + { + units.CopyTo(sharedArray, 0); + DivideCore(dividend, new ReadOnlySpan(sharedArray, 0, count), finalResult.AsSpan()); + return finalResult; + } + finally + { + ArrayPool.Shared.Return(sharedArray); + } + } + public static ICollection Divide(this double dividend, ICollection units) + where T : struct, IMensuraUnit, IEquatable + { + if (units is null) return null!; + int count = units.Count; + if (count == 0) return []; + if (units is T?[] array) return dividend.Divide(array); + if (units is List list) return dividend.Divide(list); + + var resultArray = new T?[count]; + T?[] sharedNullableArray = ArrayPool.Shared.Rent(count); + try + { + units.CopyTo(sharedNullableArray, 0); + var srcSpan = new ReadOnlySpan(sharedNullableArray, 0, count); + dividend.Divide(srcSpan, resultArray.AsSpan()); + return resultArray; + } + finally + { + ArrayPool.Shared.Return(sharedNullableArray); + } + } + + // === IReadOnlyCollection === + public static IReadOnlyCollection Divide(this IReadOnlyCollection units, double divisor) + where T : struct, IMensuraUnit, IEquatable + { + if (units is null) return null!; + int count = units.Count; + if (count == 0) return []; + if (units is T[] array) return array.Divide(divisor); + if (units is List list) return list.Divide(divisor); + + T[] sharedArray = ArrayPool.Shared.Rent(count); + var finalResult = new T[count]; + try + { + units.CopyTo(sharedArray, 0); + DivideCore(new ReadOnlySpan(sharedArray, 0, count), divisor, finalResult.AsSpan()); + return finalResult; + } + finally + { + ArrayPool.Shared.Return(sharedArray); + } + } + public static IReadOnlyCollection Divide(this IReadOnlyCollection units, double divisor) + where T : struct, IMensuraUnit, IEquatable + { + if (units is null) return null!; + int count = units.Count; + if (count == 0) return []; + if (units is T?[] array) return array.Divide(divisor); + if (units is List list) return list.Divide(divisor); + + var resultArray = new T?[count]; + T?[] sharedNullableArray = ArrayPool.Shared.Rent(count); + try + { + units.CopyTo(sharedNullableArray, 0); + var srcSpan = new ReadOnlySpan(sharedNullableArray, 0, count); + srcSpan.Divide(divisor, resultArray.AsSpan()); + return resultArray; + } + finally + { + ArrayPool.Shared.Return(sharedNullableArray); + } + } + public static IReadOnlyCollection Divide(this double dividend, IReadOnlyCollection units) + where T : struct, IMensuraUnit, IEquatable + { + if (units is null) return null!; + int count = units.Count; + if (count == 0) return []; + if (units is T[] array) return dividend.Divide(array); + if (units is List list) return dividend.Divide(list); + + T[] sharedArray = ArrayPool.Shared.Rent(count); + var finalResult = new T[count]; + try + { + units.CopyTo(sharedArray, 0); + DivideCore(dividend, new ReadOnlySpan(sharedArray, 0, count), finalResult.AsSpan()); + return finalResult; + } + finally + { + ArrayPool.Shared.Return(sharedArray); + } + } + public static IReadOnlyCollection Divide(this double dividend, IReadOnlyCollection units) + where T : struct, IMensuraUnit, IEquatable + { + if (units is null) return null!; + int count = units.Count; + if (count == 0) return []; + if (units is T?[] array) return dividend.Divide(array); + if (units is List list) return dividend.Divide(list); + + var resultArray = new T?[count]; + T?[] sharedNullableArray = ArrayPool.Shared.Rent(count); + try + { + units.CopyTo(sharedNullableArray, 0); + var srcSpan = new ReadOnlySpan(sharedNullableArray, 0, count); + dividend.Divide(srcSpan, resultArray.AsSpan()); + return resultArray; + } + finally + { + ArrayPool.Shared.Return(sharedNullableArray); + } + } + + // === IEnumerable + yeild === + static IEnumerable DivideIterator(IEnumerable units, double divisor) + where T : struct, IMensuraUnit, IEquatable + { + double invDivisor = 1.0 / divisor; + foreach (var item in units) + yield return (item.ToDouble() / invDivisor).ToUnit(); + } + static IEnumerable DivideNullableIterator(IEnumerable units, double divisor) + where T : struct, IMensuraUnit, IEquatable + { + double invDivisor = 1.0 / divisor; + foreach (T? item in units) + yield return item.HasValue + ? (item.Value.ToDouble() / invDivisor).ToUnit() : null; + } + static IEnumerable DivideIterator(double dividend, IEnumerable units) + where T : struct, IMensuraUnit, IEquatable + { + foreach (var item in units) + yield return (dividend / item.ToDouble()).ToUnit(); + } + static IEnumerable DivideNullableIterator(double dividend, IEnumerable units) + where T : struct, IMensuraUnit, IEquatable + { + foreach (T? item in units) + yield return item.HasValue + ? (dividend / item.Value.ToDouble()).ToUnit() : null; + } + + // === IEnumerable === + public static IEnumerable Divide(this IEnumerable units, double divisor) + where T : struct, IMensuraUnit, IEquatable + { + if (units is null) return null!; + if (units is T[] array) return array.Divide(divisor); + if (units is List list) return list.Divide(divisor); + if (units is ICollection col) return col.Divide(divisor); + if (units is IReadOnlyCollection roc) return roc.Divide(divisor); + return DivideIterator(units, divisor); + } + public static IEnumerable Divide(this IEnumerable units, double divisor) + where T : struct, IMensuraUnit, IEquatable + { + if (units is null) return null!; + if (units is T?[] array) return array.Divide(divisor); + if (units is List list) return list.Divide(divisor); + if (units is ICollection col) return col.Divide(divisor); + if (units is IReadOnlyCollection roc) return roc.Divide(divisor); + return DivideNullableIterator(units, divisor); + } + public static IEnumerable Divide(this double dividend, IEnumerable units) + where T : struct, IMensuraUnit, IEquatable + { + if (units is null) return null!; + if (units is T[] array) return dividend.Divide(array); + if (units is List list) return dividend.Divide(list); + if (units is ICollection col) return dividend.Divide(col); + if (units is IReadOnlyCollection roc) return dividend.Divide(roc); + return DivideIterator(dividend, units); + } + public static IEnumerable Divide(this double dividend, IEnumerable units) + where T : struct, IMensuraUnit, IEquatable + { + if (units is null) return null!; + if (units is T?[] array) return dividend.Divide(array); + if (units is List list) return dividend.Divide(list); + if (units is ICollection col) return dividend.Divide(col); + if (units is IReadOnlyCollection roc) return dividend.Divide(roc); + return DivideNullableIterator(dividend, units); + } + + + + + // ========================================== + // === PLUS === + // ========================================== + + public static T[] Plus(this T[] units, double summand) + where T : struct, IMensuraUnit, IEquatable + { + if (units is null) return null!; + int len = units.Length; + if (len == 0) return []; + + var result = new T[len]; + PlusCore(units, summand, result); + return result; + } + public static T?[] Plus(this T?[] units, double summand) + where T : struct, IMensuraUnit, IEquatable + { + if (units is null) return null!; + int len = units.Length; + if (len == 0) return []; + + // Выделяем чистую память (0 аллокаций логики, только сам массив) + var result = new T?[len]; + + for (int i = 0; i < len; i++) + { + // Читаем из исходного по значению (бесплатно для 8 байт) + T? item = units[i]; + if (item.HasValue) + { + // Пишем напрямую в результат по ref-ссылке (всего 1 запись в память!) + ref var dst = ref result[i]; + dst = (item.Value.ToDouble() + summand).ToUnit(); + } + } + return result; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T[] Plus(this double summand, T[] units) + where T : struct, IMensuraUnit, IEquatable => units.Plus(summand); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T?[] Plus(this double summand, T?[] units) + where T : struct, IMensuraUnit, IEquatable => units.Plus(summand); + + // === ReadOnlySpan === + public static void Plus(this ReadOnlySpan units, double summand, Span destination) + where T : struct, IMensuraUnit, IEquatable + { + if (units.Length > destination.Length) + throw new ArgumentException("Целевой буфер destination меньше исходного source."); + Extensions.PlusCore(units, summand, destination); + } + public static void Plus(this ReadOnlySpan units, double summand, Span destination) + where T : struct, IMensuraUnit, IEquatable + { + if (units.IsEmpty) return; + + int len = units.Length; + + // Получаем прямые неуправляемые ref-ссылки на начало буферов за 0 тактов + ref var srcRef = ref MemoryMarshal.GetReference(units); + ref var dstRef = ref MemoryMarshal.GetReference(destination); + + int i = 0; + int unrollEnd = len & ~3; // Граница развернутого цикла (кратная 4) + + // 1. ОСНОВНОЙ ЦИКЛ: Обрабатываем конвейером по 4 элемента за итерацию + for (; i < unrollEnd; i += 4) + { + T? u0 = Unsafe.Add(ref srcRef, i); + T? u1 = Unsafe.Add(ref srcRef, i + 1); + T? u2 = Unsafe.Add(ref srcRef, i + 2); + T? u3 = Unsafe.Add(ref srcRef, i + 3); + + // Получаем ref-ссылки на ячейки назначения (zero-cost адресация) + ref var d0 = ref Unsafe.Add(ref dstRef, i); + ref var d1 = ref Unsafe.Add(ref dstRef, i + 1); + ref var d2 = ref Unsafe.Add(ref dstRef, i + 2); + ref var d3 = ref Unsafe.Add(ref dstRef, i + 3); + + // Пишем строго по месту. Если HasValue == false, в ячейку dX запишется null (сбросятся байты флага) + d0 = u0.HasValue ? (u0.Value.ToDouble() + summand).ToUnit() : null; + d1 = u1.HasValue ? (u1.Value.ToDouble() + summand).ToUnit() : null; + d2 = u2.HasValue ? (u2.Value.ToDouble() + summand).ToUnit() : null; + d3 = u3.HasValue ? (u3.Value.ToDouble() + summand).ToUnit() : null; + } + + // 2. ХВОСТ ЦИКЛА: Довычисляем остаток элементов (от 1 до 3 штук) + for (; i < len; i++) + { + T? unit = Unsafe.Add(ref srcRef, i); + ref var dst = ref Unsafe.Add(ref dstRef, i); + + dst = unit.HasValue ? (unit.Value.ToDouble() + summand).ToUnit() : null; + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Plus(this double summand, ReadOnlySpan units, Span destination) + where T : struct, IMensuraUnit, IEquatable => units.Plus(summand, destination); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Plus(this double summand, ReadOnlySpan units, Span destination) + where T : struct, IMensuraUnit, IEquatable => units.Plus(summand, destination); + + // === List === + public static List Plus(this List units, double summand) + where T : struct, IMensuraUnit, IEquatable + { + if (units is null) return null!; + int len = units.Count; + if (len == 0) return []; + + var result = new List(len); + result.SetCountUnsafe(len); + Extensions.PlusCore(CollectionsMarshal.AsSpan(units), summand, CollectionsMarshal.AsSpan(result)); + return result; + } + public static List Plus(this List units, double summand) + where T : struct, IMensuraUnit, IEquatable + { + if (units is null) return null!; + int count = units.Count; + if (count == 0) return []; + + var resultArray = new T?[count]; + ReadOnlySpan srcSpan = CollectionsMarshal.AsSpan(units); + for (int i = 0; i < count; i++) + { + T? item = srcSpan[i]; + if (item.HasValue) + { + ref var dst = ref resultArray[i]; + dst = (item.Value.ToDouble() + summand).ToUnit(); + } + } + return [.. resultArray]; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static List Plus(this double summand, List units) + where T : struct, IMensuraUnit, IEquatable => units.Plus(summand); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static List Plus(this double summand, List units) + where T : struct, IMensuraUnit, IEquatable => units.Plus(summand); + + // === ICollection === + public static ICollection Plus(this ICollection units, double summand) + where T : struct, IMensuraUnit, IEquatable + { + if (units is null) return null!; + int count = units.Count; + if (count == 0) return []; + if (units is T[] array) return array.Plus(summand); + if (units is List list) return list.Plus(summand); + + T[] sharedArray = ArrayPool.Shared.Rent(count); + var finalResult = new T[count]; + try + { + units.CopyTo(sharedArray, 0); + PlusCore(new ReadOnlySpan(sharedArray, 0, count), summand, finalResult.AsSpan()); + return finalResult; + } + finally + { + ArrayPool.Shared.Return(sharedArray); + } + } + public static ICollection Plus(this ICollection units, double summand) + where T : struct, IMensuraUnit, IEquatable + { + if (units is null) return null!; + int count = units.Count; + if (count == 0) return []; + if (units is T?[] array) return array.Plus(summand); + if (units is List list) return list.Plus(summand); + + var resultArray = new T?[count]; + T?[] sharedNullableArray = ArrayPool.Shared.Rent(count); + try + { + units.CopyTo(sharedNullableArray, 0); + var srcSpan = new ReadOnlySpan(sharedNullableArray, 0, count); + srcSpan.Plus(summand, resultArray.AsSpan()); + return resultArray; + } + finally + { + ArrayPool.Shared.Return(sharedNullableArray); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ICollection Plus(this double summand, ICollection units) + where T : struct, IMensuraUnit, IEquatable => units.Plus(summand); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ICollection Plus(this double summand, ICollection units) + where T : struct, IMensuraUnit, IEquatable => units.Plus(summand); + + // === IReadOnlyCollection === + public static IReadOnlyCollection Plus(this IReadOnlyCollection units, double summand) + where T : struct, IMensuraUnit, IEquatable + { + if (units is null) return null!; + int count = units.Count; + if (count == 0) return []; + if (units is T[] array) return array.Plus(summand); + if (units is List list) return list.Plus(summand); + + T[] sharedArray = ArrayPool.Shared.Rent(count); + var finalResult = new T[count]; + try + { + units.CopyTo(sharedArray, 0); + PlusCore(new ReadOnlySpan(sharedArray, 0, count), summand, finalResult.AsSpan()); + return finalResult; + } + finally + { + ArrayPool.Shared.Return(sharedArray); + } + } + public static IReadOnlyCollection Plus(this IReadOnlyCollection units, double summand) + where T : struct, IMensuraUnit, IEquatable + { + if (units is null) return null!; + int count = units.Count; + if (count == 0) return []; + if (units is T?[] array) return array.Plus(summand); + if (units is List list) return list.Plus(summand); + + var resultArray = new T?[count]; + T?[] sharedNullableArray = ArrayPool.Shared.Rent(count); + try + { + units.CopyTo(sharedNullableArray, 0); + var srcSpan = new ReadOnlySpan(sharedNullableArray, 0, count); + srcSpan.Plus(summand, resultArray.AsSpan()); + return resultArray; + } + finally + { + ArrayPool.Shared.Return(sharedNullableArray); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static IReadOnlyCollection Plus(this double summand, IReadOnlyCollection units) + where T : struct, IMensuraUnit, IEquatable => units.Plus(summand); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static IReadOnlyCollection Plus(this double summand, IReadOnlyCollection units) + where T : struct, IMensuraUnit, IEquatable => units.Plus(summand); + + // === IEnumerable + yeild === + static IEnumerable PlusIterator(IEnumerable units, double summand) + where T : struct, IMensuraUnit, IEquatable + { + foreach (var item in units) + yield return (item.ToDouble() + summand).ToUnit(); + } + static IEnumerable PlusNullableIterator(IEnumerable units, double summand) + where T : struct, IMensuraUnit, IEquatable + { + foreach (T? item in units) + yield return item.HasValue + ? (item.Value.ToDouble() + summand).ToUnit() : null; + } + + // === IEnumerable === + public static IEnumerable Plus(this IEnumerable units, double summand) + where T : struct, IMensuraUnit, IEquatable + { + if (units is null) return null!; + if (units is T[] array) return array.Plus(summand); + if (units is List list) return list.Plus(summand); + if (units is ICollection col) return col.Plus(summand); + if (units is IReadOnlyCollection roc) return roc.Plus(summand); + return PlusIterator(units, summand); + } + public static IEnumerable Plus(this IEnumerable units, double summand) + where T : struct, IMensuraUnit, IEquatable + { + if (units is null) return null!; + if (units is T?[] array) return array.Plus(summand); + if (units is List list) return list.Plus(summand); + if (units is ICollection col) return col.Plus(summand); + if (units is IReadOnlyCollection roc) return roc.Plus(summand); + return PlusNullableIterator(units, summand); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static IEnumerable Plus(this double summand, IEnumerable units) + where T : struct, IMensuraUnit, IEquatable => Plus(units, summand); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static IEnumerable Plus(this double summand, IEnumerable units) + where T : struct, IMensuraUnit, IEquatable => Plus(units, summand); + + + + + // ========================================== + // === MINUS === + // ========================================== + + public static T[] Minus(this T[] units, double subtrahend) + where T : struct, IMensuraUnit, IEquatable + { + if (units is null) return null!; + int len = units.Length; + if (len == 0) return []; + + var result = new T[len]; + MinusCore(units, subtrahend, result); + return result; + } + public static T?[] Minus(this T?[] units, double subtrahend) + where T : struct, IMensuraUnit, IEquatable + { + if (units is null) return null!; + int len = units.Length; + if (len == 0) return []; + + // Выделяем чистую память (0 аллокаций логики, только сам массив) + var result = new T?[len]; + for (int i = 0; i < len; i++) + { + // Читаем из исходного по значению (бесплатно для 8 байт) + T? item = units[i]; + if (item.HasValue) + { + // Пишем напрямую в результат по ref-ссылке (всего 1 запись в память!) + ref var dst = ref result[i]; + dst = (item.Value.ToDouble() - subtrahend).ToUnit(); + } + } + return result; + } + public static T[] Minus(this double minuend, T[] units) + where T : struct, IMensuraUnit, IEquatable + { + if (units is null) return null!; + int len = units.Length; + if (len == 0) return []; + + var result = new T[len]; + MinusCore(minuend, units, result); + return result; + } + public static T?[] Minus(this double minuend, T?[] units) + where T : struct, IMensuraUnit, IEquatable + { + if (units is null) return null!; + int len = units.Length; + if (len == 0) return []; + + var result = new T?[len]; + + for (int i = 0; i < len; i++) + { + T? item = units[i]; + if (item.HasValue) + { + ref var dst = ref result[i]; + dst = (minuend - item.Value.ToDouble()).ToUnit(); + } + } + return result; + } + + // === ReadOnlySpan === + public static void Minus(this ReadOnlySpan units, double subtrahend, Span destination) + where T : struct, IMensuraUnit, IEquatable + { + if (units.Length > destination.Length) + throw new ArgumentException("Целевой буфер destination меньше исходного source."); + Extensions.MinusCore(units, subtrahend, destination); + } + public static void Minus(this ReadOnlySpan units, double subtrahend, Span destination) + where T : struct, IMensuraUnit, IEquatable + { + if (units.IsEmpty) return; + + int len = units.Length; + + // Получаем прямые неуправляемые ref-ссылки на начало буферов за 0 тактов + ref var srcRef = ref MemoryMarshal.GetReference(units); + ref var dstRef = ref MemoryMarshal.GetReference(destination); + + int i = 0; + int unrollEnd = len & ~3; // Граница развернутого цикла (кратная 4) + + // 1. ОСНОВНОЙ ЦИКЛ: Обрабатываем конвейером по 4 элемента за итерацию + for (; i < unrollEnd; i += 4) + { + T? u0 = Unsafe.Add(ref srcRef, i); + T? u1 = Unsafe.Add(ref srcRef, i + 1); + T? u2 = Unsafe.Add(ref srcRef, i + 2); + T? u3 = Unsafe.Add(ref srcRef, i + 3); + + // Получаем ref-ссылки на ячейки назначения (zero-cost адресация) + ref var d0 = ref Unsafe.Add(ref dstRef, i); + ref var d1 = ref Unsafe.Add(ref dstRef, i + 1); + ref var d2 = ref Unsafe.Add(ref dstRef, i + 2); + ref var d3 = ref Unsafe.Add(ref dstRef, i + 3); + + // Пишем строго по месту. Если HasValue == false, в ячейку dX запишется null (сбросятся байты флага) + d0 = u0.HasValue ? (u0.Value.ToDouble() - subtrahend).ToUnit() : null; + d1 = u1.HasValue ? (u1.Value.ToDouble() - subtrahend).ToUnit() : null; + d2 = u2.HasValue ? (u2.Value.ToDouble() - subtrahend).ToUnit() : null; + d3 = u3.HasValue ? (u3.Value.ToDouble() - subtrahend).ToUnit() : null; + } + + // 2. ХВОСТ ЦИКЛА: Довычисляем остаток элементов (от 1 до 3 штук) + for (; i < len; i++) + { + T? unit = Unsafe.Add(ref srcRef, i); + ref var dst = ref Unsafe.Add(ref dstRef, i); + + dst = unit.HasValue ? (unit.Value.ToDouble() - subtrahend).ToUnit() : null; + } + } + public static void Minus(this double minuend, ReadOnlySpan units, Span destination) + where T : struct, IMensuraUnit, IEquatable + { + if (units.Length > destination.Length) + throw new ArgumentException("Целевой буфер destination меньше исходного source."); + Extensions.MinusCore(minuend, units, destination); + } + public static void Minus(this double minuend, ReadOnlySpan units, Span destination) + where T : struct, IMensuraUnit, IEquatable + { + if (units.IsEmpty) return; + + int len = units.Length; + + // Получаем прямые неуправляемые ref-ссылки на начало буферов за 0 тактов + ref var srcRef = ref MemoryMarshal.GetReference(units); + ref var dstRef = ref MemoryMarshal.GetReference(destination); + + int i = 0; + int unrollEnd = len & ~3; // Граница развернутого цикла (кратная 4) + + // 1. ОСНОВНОЙ ЦИКЛ: Обрабатываем конвейером по 4 элемента за итерацию + for (; i < unrollEnd; i += 4) + { + T? u0 = Unsafe.Add(ref srcRef, i); + T? u1 = Unsafe.Add(ref srcRef, i + 1); + T? u2 = Unsafe.Add(ref srcRef, i + 2); + T? u3 = Unsafe.Add(ref srcRef, i + 3); + + // Получаем ref-ссылки на ячейки назначения (zero-cost адресация) + ref var d0 = ref Unsafe.Add(ref dstRef, i); + ref var d1 = ref Unsafe.Add(ref dstRef, i + 1); + ref var d2 = ref Unsafe.Add(ref dstRef, i + 2); + ref var d3 = ref Unsafe.Add(ref dstRef, i + 3); + + // Пишем строго по месту. Если HasValue == false, в ячейку dX запишется null (сбросятся байты флага) + d0 = u0.HasValue ? (minuend - u0.Value.ToDouble()).ToUnit() : null; + d1 = u1.HasValue ? (minuend - u1.Value.ToDouble()).ToUnit() : null; + d2 = u2.HasValue ? (minuend - u2.Value.ToDouble()).ToUnit() : null; + d3 = u3.HasValue ? (minuend - u3.Value.ToDouble()).ToUnit() : null; + } + + // 2. ХВОСТ ЦИКЛА: Довычисляем остаток элементов (от 1 до 3 штук) + for (; i < len; i++) + { + T? unit = Unsafe.Add(ref srcRef, i); + ref var dst = ref Unsafe.Add(ref dstRef, i); + + dst = unit.HasValue ? (minuend - unit.Value.ToDouble()).ToUnit() : null; + } + } + + // === List === + public static List Minus(this List units, double subtrahend) + where T : struct, IMensuraUnit, IEquatable + { + if (units is null) return null!; + int len = units.Count; + if (len == 0) return []; + + var result = new List(len); + result.SetCountUnsafe(len); + Extensions.MinusCore(CollectionsMarshal.AsSpan(units), subtrahend, CollectionsMarshal.AsSpan(result)); + return result; + } + public static List Minus(this List units, double subtrahend) + where T : struct, IMensuraUnit, IEquatable + { + if (units is null) return null!; + int count = units.Count; + if (count == 0) return []; + + var resultArray = new T?[count]; + ReadOnlySpan srcSpan = CollectionsMarshal.AsSpan(units); + for (int i = 0; i < count; i++) + { + T? item = srcSpan[i]; + if (item.HasValue) + { + ref var dst = ref resultArray[i]; + dst = (item.Value.ToDouble() - subtrahend).ToUnit(); + } + } + return [.. resultArray]; + } + public static List Minus(this double minuend, List units) + where T : struct, IMensuraUnit, IEquatable + { + if (units is null) return null!; + int len = units.Count; + if (len == 0) return []; + + var result = new List(len); + result.SetCountUnsafe(len); + Extensions.MinusCore(minuend, CollectionsMarshal.AsSpan(units), CollectionsMarshal.AsSpan(result)); + return result; + } + public static List Minus(this double minuend, List units) + where T : struct, IMensuraUnit, IEquatable + { + if (units is null) return null!; + int count = units.Count; + if (count == 0) return []; + + var resultArray = new T?[count]; + ReadOnlySpan srcSpan = CollectionsMarshal.AsSpan(units); + for (int i = 0; i < count; i++) + { + T? item = srcSpan[i]; + if (item.HasValue) + { + ref var dst = ref resultArray[i]; + dst = (minuend - item.Value.ToDouble()).ToUnit(); + } + } + return [.. resultArray]; + } + + // === ICollection === + public static ICollection Minus(this ICollection units, double subtrahend) + where T : struct, IMensuraUnit, IEquatable + { + if (units is null) return null!; + int count = units.Count; + if (count == 0) return []; + if (units is T[] array) return array.Minus(subtrahend); + if (units is List list) return list.Minus(subtrahend); + + T[] sharedArray = ArrayPool.Shared.Rent(count); + var finalResult = new T[count]; + try + { + units.CopyTo(sharedArray, 0); + MinusCore(new ReadOnlySpan(sharedArray, 0, count), subtrahend, finalResult.AsSpan()); + return finalResult; + } + finally + { + ArrayPool.Shared.Return(sharedArray); + } + } + public static ICollection Minus(this ICollection units, double subtrahend) + where T : struct, IMensuraUnit, IEquatable + { + if (units is null) return null!; + int count = units.Count; + if (count == 0) return []; + if (units is T?[] array) return array.Minus(subtrahend); + if (units is List list) return list.Minus(subtrahend); + + var resultArray = new T?[count]; + T?[] sharedNullableArray = ArrayPool.Shared.Rent(count); + try + { + units.CopyTo(sharedNullableArray, 0); + var srcSpan = new ReadOnlySpan(sharedNullableArray, 0, count); + srcSpan.Minus(subtrahend, resultArray.AsSpan()); + return resultArray; + } + finally + { + ArrayPool.Shared.Return(sharedNullableArray); + } + } + public static ICollection Minus(this double minuend, ICollection units) + where T : struct, IMensuraUnit, IEquatable + { + if (units is null) return null!; + int count = units.Count; + if (count == 0) return []; + if (units is T[] array) return minuend.Minus(array); + if (units is List list) return minuend.Minus(list); + + T[] sharedArray = ArrayPool.Shared.Rent(count); + var finalResult = new T[count]; + try + { + units.CopyTo(sharedArray, 0); + MinusCore(minuend, new ReadOnlySpan(sharedArray, 0, count), finalResult.AsSpan()); + return finalResult; + } + finally + { + ArrayPool.Shared.Return(sharedArray); + } + } + public static ICollection Minus(this double minuend, ICollection units) + where T : struct, IMensuraUnit, IEquatable + { + if (units is null) return null!; + int count = units.Count; + if (count == 0) return []; + if (units is T?[] array) return minuend.Minus(array); + if (units is List list) return minuend.Minus(list); + + var resultArray = new T?[count]; + T?[] sharedNullableArray = ArrayPool.Shared.Rent(count); + try + { + units.CopyTo(sharedNullableArray, 0); + var srcSpan = new ReadOnlySpan(sharedNullableArray, 0, count); + minuend.Minus(srcSpan, resultArray.AsSpan()); + return resultArray; + } + finally + { + ArrayPool.Shared.Return(sharedNullableArray); + } + } + + // === IReadOnlyCollection === + public static IReadOnlyCollection Minus(this IReadOnlyCollection units, double subtrahend) + where T : struct, IMensuraUnit, IEquatable + { + if (units is null) return null!; + int count = units.Count; + if (count == 0) return []; + if (units is T[] array) return array.Minus(subtrahend); + if (units is List list) return list.Minus(subtrahend); + + T[] sharedArray = ArrayPool.Shared.Rent(count); + var finalResult = new T[count]; + try + { + units.CopyTo(sharedArray, 0); + MinusCore(new ReadOnlySpan(sharedArray, 0, count), subtrahend, finalResult.AsSpan()); + return finalResult; + } + finally + { + ArrayPool.Shared.Return(sharedArray); + } + } + public static IReadOnlyCollection Minus(this IReadOnlyCollection units, double subtrahend) + where T : struct, IMensuraUnit, IEquatable + { + if (units is null) return null!; + int count = units.Count; + if (count == 0) return []; + if (units is T?[] array) return array.Minus(subtrahend); + if (units is List list) return list.Minus(subtrahend); + + var resultArray = new T?[count]; + T?[] sharedNullableArray = ArrayPool.Shared.Rent(count); + try + { + units.CopyTo(sharedNullableArray, 0); + var srcSpan = new ReadOnlySpan(sharedNullableArray, 0, count); + srcSpan.Minus(subtrahend, resultArray.AsSpan()); + return resultArray; + } + finally + { + ArrayPool.Shared.Return(sharedNullableArray); + } + } + public static IReadOnlyCollection Minus(this double minuend, IReadOnlyCollection units) + where T : struct, IMensuraUnit, IEquatable + { + if (units is null) return null!; + int count = units.Count; + if (count == 0) return []; + if (units is T[] array) return minuend.Minus(array); + if (units is List list) return minuend.Minus(list); + + T[] sharedArray = ArrayPool.Shared.Rent(count); + var finalResult = new T[count]; + try + { + units.CopyTo(sharedArray, 0); + MinusCore(minuend, new ReadOnlySpan(sharedArray, 0, count), finalResult.AsSpan()); + return finalResult; + } + finally + { + ArrayPool.Shared.Return(sharedArray); + } + } + public static IReadOnlyCollection Minus(this double minuend, IReadOnlyCollection units) + where T : struct, IMensuraUnit, IEquatable + { + if (units is null) return null!; + int count = units.Count; + if (count == 0) return []; + if (units is T?[] array) return minuend.Minus(array); + if (units is List list) return minuend.Minus(list); + + var resultArray = new T?[count]; + T?[] sharedNullableArray = ArrayPool.Shared.Rent(count); + try + { + units.CopyTo(sharedNullableArray, 0); + var srcSpan = new ReadOnlySpan(sharedNullableArray, 0, count); + minuend.Minus(srcSpan, resultArray.AsSpan()); + return resultArray; + } + finally + { + ArrayPool.Shared.Return(sharedNullableArray); + } + } + + // === IEnumerable + yeild === + static IEnumerable MinusIterator(IEnumerable units, double subtrahend) + where T : struct, IMensuraUnit, IEquatable + { + foreach (var item in units) + yield return (item.ToDouble() - subtrahend).ToUnit(); + } + static IEnumerable MinusNullableIterator(IEnumerable units, double subtrahend) + where T : struct, IMensuraUnit, IEquatable + { + foreach (T? item in units) + yield return item.HasValue + ? (item.Value.ToDouble() - subtrahend).ToUnit() : null; + } + static IEnumerable MinusIterator(double minuend, IEnumerable units) + where T : struct, IMensuraUnit, IEquatable + { + foreach (var item in units) + yield return (minuend - item.ToDouble()).ToUnit(); + } + static IEnumerable MinusNullableIterator(double minuend, IEnumerable units) + where T : struct, IMensuraUnit, IEquatable + { + foreach (T? item in units) + yield return item.HasValue + ? (minuend - item.Value.ToDouble()).ToUnit() : null; + } + + // === IEnumerable === + public static IEnumerable Minus(this IEnumerable units, double subtrahend) + where T : struct, IMensuraUnit, IEquatable + { + if (units is null) return null!; + if (units is T[] array) return array.Minus(subtrahend); + if (units is List list) return list.Minus(subtrahend); + if (units is ICollection col) return col.Minus(subtrahend); + if (units is IReadOnlyCollection roc) return roc.Minus(subtrahend); + return MinusIterator(units, subtrahend); + } + public static IEnumerable Minus(this IEnumerable units, double subtrahend) + where T : struct, IMensuraUnit, IEquatable + { + if (units is null) return null!; + if (units is T?[] array) return array.Minus(subtrahend); + if (units is List list) return list.Minus(subtrahend); + if (units is ICollection col) return col.Minus(subtrahend); + if (units is IReadOnlyCollection roc) return roc.Minus(subtrahend); + return MinusNullableIterator(units, subtrahend); + } + public static IEnumerable Minus(this double minuend, IEnumerable units) + where T : struct, IMensuraUnit, IEquatable + { + if (units is null) return null!; + if (units is T[] array) return minuend.Minus(array); + if (units is List list) return minuend.Minus(list); + if (units is ICollection col) return minuend.Minus(col); + if (units is IReadOnlyCollection roc) return minuend.Minus(roc); + return MinusIterator(minuend, units); + } + public static IEnumerable Minus(this double minuend, IEnumerable units) + where T : struct, IMensuraUnit, IEquatable + { + if (units is null) return null!; + if (units is T?[] array) return minuend.Minus(array); + if (units is List list) return minuend.Minus(list); + if (units is ICollection col) return minuend.Minus(col); + if (units is IReadOnlyCollection roc) return minuend.Minus(roc); + return MinusNullableIterator(minuend, units); + } + + + + + // ========================================== + // === ЦЕЛАЯ СТЕПЕНЬ === + // ========================================== + + public static T[] Pow(this T[] units, int power) + where T : struct, IMensuraUnit, IEquatable + { + if (units is null) return null!; + int len = units.Length; + if (len == 0) return []; + + var result = new T[len]; + Extensions.PowCore(units, power, result); + return result; + } + public static T?[] Pow(this T?[] units, int power) + where T : struct, IMensuraUnit, IEquatable + { + if (units is null) return null!; + int len = units.Length; + if (len == 0) return []; + + var result = new T?[len]; + for (int i = 0; i < len; i++) + { + ref var item = ref result[i]; + if (item.HasValue) + item = item.Value.ToDouble().QuickPow(power).ToUnit(); + } + return result; + } + + // === ReadOnlySpan === + public static Span Pow(this ReadOnlySpan units, int power) + where T : struct, IMensuraUnit, IEquatable + { + if (units.Length == 0) return []; + var result = new T[units.Length]; + Extensions.PowCore(units, power, result); + return result; + } + public static Span Pow(this ReadOnlySpan units, int power) + where T : struct, IMensuraUnit, IEquatable + { + int len = units.Length; + if (len == 0) return []; + var result = new T?[len]; + units.CopyTo(result); + for (int i = 0; i < len; i++) + { + ref var item = ref result[i]; + if (item.HasValue) item = item.Value.ToDouble().QuickPow(power).ToUnit(); + } + return result; + } + + // === List === + public static List Pow(this List units, int power) + where T : struct, IMensuraUnit, IEquatable + { + if (units is null) return null!; + int len = units.Count; + if (len == 0) return []; + + var result = new List(len); + result.SetCountUnsafe(len); + Extensions.PowCore(CollectionsMarshal.AsSpan(units), power, CollectionsMarshal.AsSpan(result)); + return result; + } + public static List Pow(this List units, int power) + where T : struct, IMensuraUnit, IEquatable + { + if (units is null) return null!; + int len = units.Count; + if (len == 0) return []; + + var result = new List(units); + var dstSpan = CollectionsMarshal.AsSpan(result); + for (int i = 0; i < len; i++) + { + ref var item = ref dstSpan[i]; + if (item.HasValue) item = item.Value.ToDouble().QuickPow(power).ToUnit(); + } + return result; + } + + // === ICollection === + public static ICollection Pow(this ICollection units, int power) + where T : struct, IMensuraUnit, IEquatable + { + if (units is null) return null!; + if (units is T[] array) return array.Pow(power); + if (units is List list) return list.Pow(power); + + ICollection result = []; + foreach (var item in units) + result.Add(item.ToDouble().QuickPow(power).ToUnit()); + return result; + } + public static ICollection Pow(this ICollection units, int power) + where T : struct, IMensuraUnit, IEquatable + { + if (units is null) return null!; + if (units is T?[] array) return array.Pow(power); + if (units is List list) return list.Pow(power); + + ICollection result = []; + foreach (var item in units) + result.Add((item.HasValue ? item.Value.ToDouble().QuickPow(power) : 0d).ToUnit()); + return result; + } + + // === IReadOnlyCollection === + public static IReadOnlyCollection Pow(this IReadOnlyCollection units, int power) + where T : struct, IMensuraUnit, IEquatable + { + if (units is null) return null!; + if (units is T[] array) return array.Pow(power); + if (units is List list) return list.Pow(power); + + IReadOnlyCollection result = []; + foreach (var item in units) + result.Add(item.ToDouble().QuickPow(power).ToUnit()); + return result; + } + public static IReadOnlyCollection Pow(this IReadOnlyCollection units, int power) + where T : struct, IMensuraUnit, IEquatable + { + if (units is null) return null!; + if (units is T?[] array) return array.Pow(power); + if (units is List list) return list.Pow(power); + + IReadOnlyCollection result = []; + foreach (var item in units) + result.Add((item.HasValue ? item.Value.ToDouble().QuickPow(power) : 0d).ToUnit()); + return result; + } + + // === IEnumerable + yeild === + static IEnumerable PowIterator(IEnumerable units, int power) + where T : struct, IMensuraUnit, IEquatable + { + foreach (var item in units) + yield return (item.ToDouble().QuickPow(power)).ToUnit(); + } + static IEnumerable PowNullableIterator(IEnumerable units, int power) + where T : struct, IMensuraUnit, IEquatable + { + foreach (var item in units) + yield return item.HasValue + ? item.Value.ToDouble().QuickPow(power).ToUnit() : null; + } + + // === IEnumerable === + public static IEnumerable Pow(this IEnumerable units, int power) + where T : struct, IMensuraUnit, IEquatable + { + if (units is null) return null!; + if (units is T[] array) return array.Pow(power); + if (units is List list) return list.Pow(power); + if (units is ICollection col) return col.Pow(power); + if (units is IReadOnlyCollection roc) return roc.Pow(power); + else return PowIterator(units, power); + } + public static IEnumerable Pow(this IEnumerable units, int power) + where T : struct, IMensuraUnit, IEquatable + { + if (units is null) return null!; + if (units is T?[] array) return array.Pow(power); + if (units is List list) return list.Pow(power); + if (units is ICollection col) return col.Pow(power); + if (units is IReadOnlyCollection roc) return roc.Pow(power); + else return PowNullableIterator(units, power); + } @@ -77,261 +3297,264 @@ public static partial class Extensions - //internal static U Protect(this U? metric) where U : class, IMetric, new() => metric ?? new(); - //internal static C Protect(this C? collection) - // where C : IMetricCollection, new() where U : class, IMetric, new() => collection ?? new(); - //public static C MetricSelect(this C? collection, Func? selector) - // where C : IMetricCollection, new() where U : class, IMetric, new() - //{ - // var source = (collection ??= new()); - // var nColl = (C)source.CreateByInstanceU(source.Count); - // if (selector is not null) - // { - // for (int i = 0; i < nColl.Count; i++) - // nColl[i] = selector(source[i]); - // return nColl; - // } - // return new(); - //} - //public static IEnumerable MetricSelect(this IMetricCollection collection, Func selector) - // where U : class, IMetric, new() - //{ - // if (collection is not null) - // { - // if (selector is not null) - // return collection.Select(selector); - // return collection.Select(u => double.NaN); - // } - // else return []; - //} + + //internal static U Protect(this U? metric) where U : class, IMetric, new() => metric ?? new(); + //internal static C Protect(this C? collection) + // where C : IMetricCollection, new() where U : class, IMetric, new() => collection ?? new(); + + + //public static C MetricSelect(this C? collection, Func? selector) + // where C : IMetricCollection, new() where U : class, IMetric, new() + //{ + // var source = (collection ??= new()); + // var nColl = (C)source.CreateByInstanceU(source.Count); + // if (selector is not null) + // { + // for (int i = 0; i < nColl.Count; i++) + // nColl[i] = selector(source[i]); + // return nColl; + // } + // return new(); + //} + //public static IEnumerable MetricSelect(this IMetricCollection collection, Func selector) + // where U : class, IMetric, new() + //{ + // if (collection is not null) + // { + // if (selector is not null) + // return collection.Select(selector); + // return collection.Select(u => double.NaN); + // } + // else return []; + //} - //public static IMetricCollection MetricSelect(this IMetricCollection? collection, Func? selector) - // where Ux : class, IMetric, new() where Uz : class, IMetric, new() - //{ - // if (collection is not null && selector is not null) - // { - // var destCollection = collection.CreateByInstance(collection.Count); - // for (int i = 0; i < collection.Count; i++) - // destCollection[i] = selector(collection[i]); - // return destCollection; - // } - // return null!; - //} - //public static MetricCollection MetricSelect(this MetricCollection? collection, Func? selector) - // where Ux : class, IMetric, new() where Uz : class, IMetric, new() - //{ - // if (collection is not null && selector is not null) - // { - // var destCollection = collection.CreateByInstance(collection.Count()); - // for (int i = 0; i < collection.Count(); i++) - // destCollection[i] = selector(collection[i]); - // return destCollection; - // } - // return null!; - //} - //public static MetricArray MetricSelect(this MetricArray? collection, Func? selector) - // where Ux : class, IMetric, new() where Uz : class, IMetric, new() - //{ - // var coll = collection?.ToArray(); - // if (coll is not null && selector is not null) - // { - // var destCollection = new MetricArray(coll.Length); - // for (int i = 0; i < coll.Length; i++) - // destCollection[i] = selector(coll[i]); - // return destCollection; - // } - // return null!; - //} - //public static MetricList MetricSelect(this MetricList? collection, Func? selector) - // where Ux : class, IMetric, new() where Uz : class, IMetric, new() - //{ - // if (collection is not null && selector is not null) - // { - // var destCollection = new MetricList(collection.Count); - // for (int i = 0; i < collection.Count; i++) - // destCollection[i] = selector(collection[i]); - // return destCollection; - // } - // return null!; - //} - //public static MetricObservableCollection MetricSelect(this MetricObservableCollection? collection, Func? selector) - // where Ux : class, IMetric, new() where Uz : class, IMetric, new() - //{ - // if (collection is not null && selector is not null) - // { - // var destCollection = new MetricObservableCollection(collection.Count); - // for (int i = 0; i < collection.Count; i++) - // destCollection[i] = selector(collection[i]); - // return destCollection; - // } - // return null!; - //} + //public static IMetricCollection MetricSelect(this IMetricCollection? collection, Func? selector) + // where Ux : class, IMetric, new() where Uz : class, IMetric, new() + //{ + // if (collection is not null && selector is not null) + // { + // var destCollection = collection.CreateByInstance(collection.Count); + // for (int i = 0; i < collection.Count; i++) + // destCollection[i] = selector(collection[i]); + // return destCollection; + // } + // return null!; + //} + //public static MetricCollection MetricSelect(this MetricCollection? collection, Func? selector) + // where Ux : class, IMetric, new() where Uz : class, IMetric, new() + //{ + // if (collection is not null && selector is not null) + // { + // var destCollection = collection.CreateByInstance(collection.Count()); + // for (int i = 0; i < collection.Count(); i++) + // destCollection[i] = selector(collection[i]); + // return destCollection; + // } + // return null!; + //} + //public static MetricArray MetricSelect(this MetricArray? collection, Func? selector) + // where Ux : class, IMetric, new() where Uz : class, IMetric, new() + //{ + // var coll = collection?.ToArray(); + // if (coll is not null && selector is not null) + // { + // var destCollection = new MetricArray(coll.Length); + // for (int i = 0; i < coll.Length; i++) + // destCollection[i] = selector(coll[i]); + // return destCollection; + // } + // return null!; + //} + //public static MetricList MetricSelect(this MetricList? collection, Func? selector) + // where Ux : class, IMetric, new() where Uz : class, IMetric, new() + //{ + // if (collection is not null && selector is not null) + // { + // var destCollection = new MetricList(collection.Count); + // for (int i = 0; i < collection.Count; i++) + // destCollection[i] = selector(collection[i]); + // return destCollection; + // } + // return null!; + //} + //public static MetricObservableCollection MetricSelect(this MetricObservableCollection? collection, Func? selector) + // where Ux : class, IMetric, new() where Uz : class, IMetric, new() + //{ + // if (collection is not null && selector is not null) + // { + // var destCollection = new MetricObservableCollection(collection.Count); + // for (int i = 0; i < collection.Count; i++) + // destCollection[i] = selector(collection[i]); + // return destCollection; + // } + // return null!; + //} - //public static double[] MetricSelect(this MetricArray collection, Func selector) - // where U : class, IMetric, new() - //{ - // var coll = collection ?? []; - // var arr = new double[coll.Length]; - // if (selector is not null) - // for (int i = 0; i < arr.Length; i++) - // arr[i] = selector(coll[i]); - // return arr; - //} - //public static List MetricSelect(this MetricList collection, Func selector) - // where U : class, IMetric, new() - //{ - // var coll = collection ?? []; - // var list = new List(coll.Count); - // if (selector is not null) - // for (int i = 0; i < list.Count; i++) - // list[i] = selector(coll[i]); - // return list; - //} - //public static ObservableCollection MetricSelect(this MetricObservableCollection collection, Func selector) - // where U : class, IMetric, new() - //{ - // var coll = collection ?? []; - // var list = new List(coll.Count); - // if (selector is not null) - // for (int i = 0; i < list.Count; i++) - // list[i] = selector(coll[i]); - // return new(list); - //} - //internal static C ForEachC(this C? collection, Func? Set) - // where C : IMetricCollection, new() where U : class, IMetric, new() - //{ - // var nColl = (C)(collection ??= new()).CreateByInstanceU(collection.Count); - // if (Set is not null) - // for (int i = 0; i < nColl.Count; i++) - // nColl[i] = Set(nColl[i]); - // return nColl; - //} - //internal static double Protect_Value(this IMetric? metric) => metric is null ? 0d : metric._Value; + //public static double[] MetricSelect(this MetricArray collection, Func selector) + // where U : class, IMetric, new() + //{ + // var coll = collection ?? []; + // var arr = new double[coll.Length]; + // if (selector is not null) + // for (int i = 0; i < arr.Length; i++) + // arr[i] = selector(coll[i]); + // return arr; + //} + //public static List MetricSelect(this MetricList collection, Func selector) + // where U : class, IMetric, new() + //{ + // var coll = collection ?? []; + // var list = new List(coll.Count); + // if (selector is not null) + // for (int i = 0; i < list.Count; i++) + // list[i] = selector(coll[i]); + // return list; + //} + //public static ObservableCollection MetricSelect(this MetricObservableCollection collection, Func selector) + // where U : class, IMetric, new() + //{ + // var coll = collection ?? []; + // var list = new List(coll.Count); + // if (selector is not null) + // for (int i = 0; i < list.Count; i++) + // list[i] = selector(coll[i]); + // return new(list); + //} + //internal static C ForEachC(this C? collection, Func? Set) + // where C : IMetricCollection, new() where U : class, IMetric, new() + //{ + // var nColl = (C)(collection ??= new()).CreateByInstanceU(collection.Count); + // if (Set is not null) + // for (int i = 0; i < nColl.Count; i++) + // nColl[i] = Set(nColl[i]); + // return nColl; + //} + //internal static double Protect_Value(this IMetric? metric) => metric is null ? 0d : metric._Value; - //public static U Min(this U? T1, U? T2) where U : class, IMetric, new() => (T1.Protect_Value() < T2.Protect_Value() ? T1 : T2).Protect(); - //public static U Min(this U T1, IEnumerable units) where U : class, IMetric, new() => (T1 ?? new()).Min((units ?? []).MaxBy(u => u.Protect_Value())); + //public static U Min(this U? T1, U? T2) where U : class, IMetric, new() => (T1.Protect_Value() < T2.Protect_Value() ? T1 : T2).Protect(); + //public static U Min(this U T1, IEnumerable units) where U : class, IMetric, new() => (T1 ?? new()).Min((units ?? []).MaxBy(u => u.Protect_Value())); - //public static U Max(this U? T1, U? T2) where U : class, IMetric, new() => (T1.Protect_Value() > T2.Protect_Value() ? T1 : T2).Protect(); - //public static U Max(this U T1, IEnumerable units) where U : class, IMetric, new() => (T1 ?? new()).Max((units ?? []).MaxBy(u => u.Protect_Value())); + //public static U Max(this U? T1, U? T2) where U : class, IMetric, new() => (T1.Protect_Value() > T2.Protect_Value() ? T1 : T2).Protect(); + //public static U Max(this U T1, IEnumerable units) where U : class, IMetric, new() => (T1 ?? new()).Max((units ?? []).MaxBy(u => u.Protect_Value())); - ////internal static double ToDouble(this double number) => number; - ////internal static double ToDouble(this double? number) => number ?? 0d; - ////internal static double ToDouble(this N number) where N : INumber => Convert.ToDouble(number); - ////internal static double ToDouble(this N? number) where N : struct, INumber => number is not null ? Convert.ToDouble(number) : 0d; + ////internal static double ToDouble(this double number) => number; + ////internal static double ToDouble(this double? number) => number ?? 0d; + ////internal static double ToDouble(this N number) where N : INumber => Convert.ToDouble(number); + ////internal static double ToDouble(this N? number) where N : struct, INumber => number is not null ? Convert.ToDouble(number) : 0d; - //internal static IEnumerable MetricSelect(this double[] nums, Func selector) where U : class, IMetric, new() => nums.Select(selector); - //internal static IEnumerable MetricSelect(this double?[] nums, Func selector) where U : class, IMetric, new() => nums.Select(num => selector(num.ToDouble())); - //internal static IEnumerable MetricSelect(this N[] nums, Func selector) where N : INumber where U : class, IMetric, new() => nums.Select(num => selector(num.ToDouble())); - //internal static IEnumerable MetricSelect(this N?[] nums, Func selector) where N : struct, INumber where U : class, IMetric, new() => nums.Select(num => selector(num.ToDouble())); + //internal static IEnumerable MetricSelect(this double[] nums, Func selector) where U : class, IMetric, new() => nums.Select(selector); + //internal static IEnumerable MetricSelect(this double?[] nums, Func selector) where U : class, IMetric, new() => nums.Select(num => selector(num.ToDouble())); + //internal static IEnumerable MetricSelect(this N[] nums, Func selector) where N : INumber where U : class, IMetric, new() => nums.Select(num => selector(num.ToDouble())); + //internal static IEnumerable MetricSelect(this N?[] nums, Func selector) where N : struct, INumber where U : class, IMetric, new() => nums.Select(num => selector(num.ToDouble())); - //internal static IEnumerable MetricSelect(this IEnumerable nums, Func selector) where U : class, IMetric, new() => nums.Select(selector); - //internal static IEnumerable MetricSelect(this IEnumerable nums, Func selector) where U : class, IMetric, new() => nums.Select(num => selector(num.ToDouble())); - //internal static IEnumerable MetricSelect(this IEnumerable nums, Func selector) where N : INumber where U : class, IMetric, new() => nums.Select(num => selector(num.ToDouble())); - //internal static IEnumerable MetricSelect(this IEnumerable nums, Func selector) where N : struct, INumber where U : class, IMetric, new() => nums.Select(num => selector(num.ToDouble())); + //internal static IEnumerable MetricSelect(this IEnumerable nums, Func selector) where U : class, IMetric, new() => nums.Select(selector); + //internal static IEnumerable MetricSelect(this IEnumerable nums, Func selector) where U : class, IMetric, new() => nums.Select(num => selector(num.ToDouble())); + //internal static IEnumerable MetricSelect(this IEnumerable nums, Func selector) where N : INumber where U : class, IMetric, new() => nums.Select(num => selector(num.ToDouble())); + //internal static IEnumerable MetricSelect(this IEnumerable nums, Func selector) where N : struct, INumber where U : class, IMetric, new() => nums.Select(num => selector(num.ToDouble())); - //public static U Clone(this U? metric) where U : class, IMetric, new() => new() { _Value = metric.Protect_Value() }; - //public static U Abs(this U? metric) where U : class, IMetric, new() => new() { _Value = Math.Abs(metric.Protect_Value()) }; + //public static U Clone(this U? metric) where U : class, IMetric, new() => new() { _Value = metric.Protect_Value() }; + //public static U Abs(this U? metric) where U : class, IMetric, new() => new() { _Value = Math.Abs(metric.Protect_Value()) }; - ///// C^2 = A^2 + B^2 - ///// C = (A^2 + B^2).Sqrt(2) - //public static U Hypotenuse(this U? A, U? B) where U : class, IMetric, new() - //{ - // var a = A.Protect_Value(); - // var b = B.Protect_Value(); - // return new U() { _Value = Math.Sqrt(a * a + b * b) }; - //} - ///// C^2 = A^2 + B^2 - ///// B = (C^2 - A^2).Sqrt(2) - //public static U KatetFromHyp(this U? A, U? C) where U : class, IMetric, new() - //{ - // var a = A.Protect_Value(); - // var c = C.Protect_Value(); - // return new U() { _Value = Math.Sqrt(c * c - a * a) }; - //} - ///// C^2 = A^2 + B^2 - ///// B = (C^2 - A^2).Sqrt(2) - //public static U KatetFromKatet(this U? C, U? A) where U : class, IMetric, new() - //{ - // var a = A.Protect_Value(); - // var c = C.Protect_Value(); - // return new U() { _Value = Math.Sqrt(c * c - a * a) }; - //} + ///// C^2 = A^2 + B^2 + ///// C = (A^2 + B^2).Sqrt(2) + //public static U Hypotenuse(this U? A, U? B) where U : class, IMetric, new() + //{ + // var a = A.Protect_Value(); + // var b = B.Protect_Value(); + // return new U() { _Value = Math.Sqrt(a * a + b * b) }; + //} + ///// C^2 = A^2 + B^2 + ///// B = (C^2 - A^2).Sqrt(2) + //public static U KatetFromHyp(this U? A, U? C) where U : class, IMetric, new() + //{ + // var a = A.Protect_Value(); + // var c = C.Protect_Value(); + // return new U() { _Value = Math.Sqrt(c * c - a * a) }; + //} + ///// C^2 = A^2 + B^2 + ///// B = (C^2 - A^2).Sqrt(2) + //public static U KatetFromKatet(this U? C, U? A) where U : class, IMetric, new() + //{ + // var a = A.Protect_Value(); + // var c = C.Protect_Value(); + // return new U() { _Value = Math.Sqrt(c * c - a * a) }; + //} - //public static Area Pow(this Length? metric, double? val = 2) => new() { _Value = Math.Pow(metric.Protect_Value(), val ?? 2) }; - //public static Length Sqrt(this Area? metric) => new() { _Value = Math.Sqrt(metric.Protect_Value()) }; + //public static Area Pow(this Length? metric, double? val = 2) => new() { _Value = Math.Pow(metric.Protect_Value(), val ?? 2) }; + //public static Length Sqrt(this Area? metric) => new() { _Value = Math.Sqrt(metric.Protect_Value()) }; - //public static U MetricSum(this IEnumerable args) where U : IMetric, new() => new() { _Value = args?.Where(t => t is not null).Sum(m => m.Protect_Value()) ?? 0d }; - //public static U MetricAverage(this IEnumerable args) where U : IMetric, new() => new() { _Value = args?.Average(m => m.Protect_Value()) ?? double.NaN }; - //public static U MetricMax(this IEnumerable args) where U : IMetric, new() => new() { _Value = args.Max(m => m.Protect_Value()) }; - //public static U MetricMin(this IEnumerable args) where U : IMetric, new() => new() { _Value = args.Min(m => m.Protect_Value()) }; + //public static U MetricSum(this IEnumerable args) where U : IMetric, new() => new() { _Value = args?.Where(t => t is not null).Sum(m => m.Protect_Value()) ?? 0d }; + //public static U MetricAverage(this IEnumerable args) where U : IMetric, new() => new() { _Value = args?.Average(m => m.Protect_Value()) ?? double.NaN }; + //public static U MetricMax(this IEnumerable args) where U : IMetric, new() => new() { _Value = args.Max(m => m.Protect_Value()) }; + //public static U MetricMin(this IEnumerable args) where U : IMetric, new() => new() { _Value = args.Min(m => m.Protect_Value()) }; - //public static C MetricSum(this IEnumerable> collections) - // where C : MetricCollection, ICreateByCapacity, new() where U : class, IMetric, new() - //{ - // var cArr = collections.ToArray(); - // C accumulator = (C)cArr.FirstOrDefault(new C()); - // for (int i = 1; i < cArr.Length; i++) - // accumulator = accumulator.FuncByPairOrOneToMany(cArr[i], (a, b) => a + b, out C _); - // return accumulator; - //} - //public static C MetricAverage(this IEnumerable> collections) - // where C : MetricCollection, ICreateByCapacity, new() where U : class, IMetric, new() - // => collections.MetricSum() / collections.Count(); + //public static C MetricSum(this IEnumerable> collections) + // where C : MetricCollection, ICreateByCapacity, new() where U : class, IMetric, new() + //{ + // var cArr = collections.ToArray(); + // C accumulator = (C)cArr.FirstOrDefault(new C()); + // for (int i = 1; i < cArr.Length; i++) + // accumulator = accumulator.FuncByPairOrOneToMany(cArr[i], (a, b) => a + b, out C _); + // return accumulator; + //} + //public static C MetricAverage(this IEnumerable> collections) + // where C : MetricCollection, ICreateByCapacity, new() where U : class, IMetric, new() + // => collections.Sum() / collections.Count(); - //public static U MetricSumBy(this IEnumerable source, Func selector) - // where U : class, IMetric, new() - //{ - // if (source is null) return new(); - // if (selector is null) throw new ArgumentNullException("selector is null"); + //public static U MetricSumBy(this IEnumerable source, Func selector) + // where U : class, IMetric, new() + //{ + // if (source is null) return new(); + // if (selector is null) throw new ArgumentNullException("selector is null"); - // return new() { _Value = source.Select(selector).Where(t => t is not null).Sum(t => t.Protect_Value()) }; - //} - //public static U MetricAverageBy(this IEnumerable source, Func selector) - // where U : class, IMetric, new() - //{ - // if (source is null) return new(); - // if (selector is null) throw new ArgumentNullException("selector is null"); + // return new() { _Value = source.Select(selector).Where(t => t is not null).Sum(t => t.Protect_Value()) }; + //} + //public static U MetricAverageBy(this IEnumerable source, Func selector) + // where U : class, IMetric, new() + //{ + // if (source is null) return new(); + // if (selector is null) throw new ArgumentNullException("selector is null"); - // return new() { _Value = source.Select(selector).Average(t => t.Protect_Value()) }; - //} + // return new() { _Value = source.Select(selector).Average(t => t.Protect_Value()) }; + //} - //public static C MetricSumBy(this IEnumerable source, Func> selector) - // where C : MetricCollection, ICreateByCapacity, new() where U : class, IMetric, new() - //{ - // if (source is null) return new(); - // if (selector is null) throw new ArgumentNullException("selector is null"); + //public static C MetricSumBy(this IEnumerable source, Func> selector) + // where C : MetricCollection, ICreateByCapacity, new() where U : class, IMetric, new() + //{ + // if (source is null) return new(); + // if (selector is null) throw new ArgumentNullException("selector is null"); - // return source.Select(selector).MetricSum(); - //} - //public static C MetricAverageBy(this IEnumerable source, Func> selector) - // where C : MetricCollection, ICreateByCapacity, new() where U : class, IMetric, new() - //{ - // if (source is null) return new(); - // if (selector is null) throw new ArgumentNullException("selector is null"); + // return source.Select(selector).Sum(); + //} + //public static C MetricAverageBy(this IEnumerable source, Func> selector) + // where C : MetricCollection, ICreateByCapacity, new() where U : class, IMetric, new() + //{ + // if (source is null) return new(); + // if (selector is null) throw new ArgumentNullException("selector is null"); - // return source.Select(selector).MetricAverage(); - //} + // return source.Select(selector).Average(); + //} - //public static MetricArray ToMetricArray(this IEnumerable source) where U : class, IMetric, new() => new(source); - //public static MetricList ToMetricList(this IEnumerable source) where U : class, IMetric, new() => new(source); + //public static MetricArray ToMetricArray(this IEnumerable source) where U : class, IMetric, new() => new(source); + //public static MetricList ToMetricList(this IEnumerable source) where U : class, IMetric, new() => new(source); } \ No newline at end of file diff --git a/QWERTYkez.Mensura/Units/Pogon/PogonXXXXXXXX.Gen.cs b/QWERTYkez.Mensura/Units/Pogon/PogonXXXXXXXX.Gen.cs index 8d08aab..c335f15 100644 --- a/QWERTYkez.Mensura/Units/Pogon/PogonXXXXXXXX.Gen.cs +++ b/QWERTYkez.Mensura/Units/Pogon/PogonXXXXXXXX.Gen.cs @@ -1,4 +1,5 @@ -global using PogonMass = QWERTYkez.Mensura.Units.Pogon.PogonMass; +#if DEBUG +global using PogonMass = QWERTYkez.Mensura.Units.Pogon.PogonMass; global using PogonMassExtensions = QWERTYkez.Mensura.Units.Pogon.PogonMassExtensions; global using PogonMassConverter = QWERTYkez.Mensura.Units.Pogon.PogonMassConverter; @@ -20,8 +21,7 @@ namespace QWERTYkez.Mensura.Units if (len == 0) return []; var result = new Mass[len]; - for (int i = 0; i < len; i++) - result[i] = right * left[i]; + PogonMassExtensions.MultiplyCore(left, right, result); return result; } public static Mass[] operator *(PogonMass[] left, Length? right) @@ -367,7 +367,7 @@ namespace QWERTYkez.Mensura.Units.Pogon - internal static void MultiplyCore(ReadOnlySpan source, Length value, Span destination) + internal static void MultiplyCore(this ReadOnlySpan source, Length value, Span destination) { var dVal = value._Value; int len = source.Length; @@ -398,7 +398,7 @@ namespace QWERTYkez.Mensura.Units.Pogon Unsafe.Add(ref dstRef, i) = Unsafe.Add(ref srcRef, i) * dVal; } } - internal static void MultiplyCore(ReadOnlySpan source, PogonMass value, Span destination) + internal static void MultiplyCore(this ReadOnlySpan source, PogonMass value, Span destination) { var dVal = value._Value; int len = source.Length; @@ -431,7 +431,7 @@ namespace QWERTYkez.Mensura.Units.Pogon } - internal static void DivideCore(ReadOnlySpan source, PogonMass divisor, Span destination) + internal static void DivideCore(this ReadOnlySpan source, PogonMass divisor, Span destination) { // 1. Проверка на ноль if (divisor.IsZero || divisor.IsNaN) @@ -466,7 +466,7 @@ namespace QWERTYkez.Mensura.Units.Pogon Unsafe.Add(ref dstRef, i) = Unsafe.Add(ref srcRef, i) / dVal; } } - internal static void DivideCore(Mass dividend, ReadOnlySpan source, Span destination) + internal static void DivideCore(this Mass dividend, ReadOnlySpan source, Span destination) { var dVal = dividend._Value; int len = source.Length; @@ -565,4 +565,5 @@ namespace QWERTYkez.Mensura.Units.Pogon return new(double_Value); } } -} \ No newline at end of file +} +#endif \ No newline at end of file diff --git a/QWERTYkez.Mensura/Units/Pogon/PogonXXXXXXXX.Ref.cs b/QWERTYkez.Mensura/Units/Pogon/PogonXXXXXXXX.Ref.cs index e38119e..08df7c2 100644 --- a/QWERTYkez.Mensura/Units/Pogon/PogonXXXXXXXX.Ref.cs +++ b/QWERTYkez.Mensura/Units/Pogon/PogonXXXXXXXX.Ref.cs @@ -1,4 +1,5 @@ -namespace QWERTYkez.Mensura.Units.Pogon; +#if DEBUG +namespace QWERTYkez.Mensura.Units.Pogon; public readonly partial record struct PogonMass { @@ -30,4 +31,5 @@ public readonly partial record struct PogonMass init => _Value = value._Value / LengthConv.KiloMeters.Multiplicator; } -} \ No newline at end of file +} +#endif \ No newline at end of file diff --git a/QWERTYkez.Mensura/Units/XXXXXXXX.Gen.cs b/QWERTYkez.Mensura/Units/XXXXXXXX.Gen.cs index 041656a..485de93 100644 --- a/QWERTYkez.Mensura/Units/XXXXXXXX.Gen.cs +++ b/QWERTYkez.Mensura/Units/XXXXXXXX.Gen.cs @@ -1,1681 +1,1368 @@ -//using System.Globalization; -//using System.Runtime.Serialization; - -//namespace QWERTYkez.Mensura.Units; - -//[JsonConverter(typeof(XXXXXXXXXXXXXXConverter))] -//public readonly partial record struct XXXXXXXXXXXXXX -//{ -// [JsonInclude, DataMember, JsonPropertyName("v")] // для JSON / EF на случай сбоев, если пробелма с _Value -// internal double _Value { get => _Value; init => _Value = value; } -// internal readonly double _Value; -// internal XXXXXXXXXXXXXX(double value) => _Value = value; - -// public override int GetHashCode() => _Value.GetHashCode(); -// public int CompareTo(XXXXXXXXXXXXXX? other) => _Value.CompareTo(other is null ? 0d : other._Value._Value); -// public int CompareTo(XXXXXXXXXXXXXX other) => _Value.CompareTo(other._Value); - -// public bool Equals(XXXXXXXXXXXXXX? 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 XXXXXXXXXXXXXX Zero { get; } = new(0d); -// public static XXXXXXXXXXXXXX Min { get; } = new(double.MinValue); -// public static XXXXXXXXXXXXXX Max { get; } = new(double.MaxValue); -// public static XXXXXXXXXXXXXX NegativeInfinity { get; } = new(double.NegativeInfinity); -// public static XXXXXXXXXXXXXX PositiveInfinity { get; } = new(double.PositiveInfinity); - - -// public static bool operator ==(XXXXXXXXXXXXXX? T1, XXXXXXXXXXXXXX? T2) => (T1 is null ? 0d : T1._Value._Value) == (T2 is null ? 0d : T2._Value._Value); -// public static bool operator !=(XXXXXXXXXXXXXX? T1, XXXXXXXXXXXXXX? T2) => (T1 is null ? 0d : T1._Value._Value) != (T2 is null ? 0d : T2._Value._Value); - -// public static bool operator <(XXXXXXXXXXXXXX? T1, XXXXXXXXXXXXXX? T2) => (T1 is null ? 0d : T1._Value._Value) < (T2 is null ? 0d : T2._Value._Value); -// public static bool operator <=(XXXXXXXXXXXXXX? T1, XXXXXXXXXXXXXX? T2) => (T1 is null ? 0d : T1._Value._Value) <= (T2 is null ? 0d : T2._Value._Value); -// public static bool operator >(XXXXXXXXXXXXXX? T1, XXXXXXXXXXXXXX? T2) => (T1 is null ? 0d : T1._Value._Value) > (T2 is null ? 0d : T2._Value._Value); -// public static bool operator >=(XXXXXXXXXXXXXX? T1, XXXXXXXXXXXXXX? T2) => (T1 is null ? 0d : T1._Value._Value) >= (T2 is null ? 0d : T2._Value._Value); - - -// public static XXXXXXXXXXXXXX operator +(XXXXXXXXXXXXXX T2) => new(+T2._Value); -// public static XXXXXXXXXXXXXX operator +(XXXXXXXXXXXXXX T1, XXXXXXXXXXXXXX T2) => new(T1._Value + T2._Value); -// public static XXXXXXXXXXXXXX operator -(XXXXXXXXXXXXXX T2) => new(-T2._Value); -// public static XXXXXXXXXXXXXX operator -(XXXXXXXXXXXXXX T1, XXXXXXXXXXXXXX T2) => new(T1._Value - T2._Value); - - -// // double -// public static XXXXXXXXXXXXXX operator *(XXXXXXXXXXXXXX T1, double T2) => new(T1._Value * T2); -// public static XXXXXXXXXXXXXX operator *(XXXXXXXXXXXXXX T1, double? T2) => T1 * (T2 ?? 0d); -// public static XXXXXXXXXXXXXX operator *(double T1, XXXXXXXXXXXXXX T2) => new(T1 * T2._Value); -// public static XXXXXXXXXXXXXX operator *(double? T1, XXXXXXXXXXXXXX T2) => (T1 ?? 0d) * T2; -// public static XXXXXXXXXXXXXX operator /(XXXXXXXXXXXXXX T1, double T2) => new(T1._Value / T2); -// public static XXXXXXXXXXXXXX operator /(XXXXXXXXXXXXXX T1, double? T2) => T1 / (T2 ?? 0d); -// public static double operator /(XXXXXXXXXXXXXX T1, XXXXXXXXXXXXXX T2) => T1._Value / T2._Value; - -// // sbyte -// public static XXXXXXXXXXXXXX operator *(XXXXXXXXXXXXXX T1, sbyte T2) => T1 * T2.ToDouble(); -// public static XXXXXXXXXXXXXX operator *(XXXXXXXXXXXXXX T1, sbyte? T2) => T1 * T2.ToDouble(); -// public static XXXXXXXXXXXXXX operator *(sbyte T1, XXXXXXXXXXXXXX T2) => T1.ToDouble() * T2; -// public static XXXXXXXXXXXXXX operator *(sbyte? T1, XXXXXXXXXXXXXX T2) => T1.ToDouble() * T2; -// public static XXXXXXXXXXXXXX operator /(XXXXXXXXXXXXXX T1, sbyte T2) => T1 / T2.ToDouble(); -// public static XXXXXXXXXXXXXX operator /(XXXXXXXXXXXXXX T1, sbyte? T2) => T1 / T2.ToDouble(); - -// // short -// public static XXXXXXXXXXXXXX operator *(XXXXXXXXXXXXXX T1, short T2) => T1 * T2.ToDouble(); -// public static XXXXXXXXXXXXXX operator *(XXXXXXXXXXXXXX T1, short? T2) => T1 * T2.ToDouble(); -// public static XXXXXXXXXXXXXX operator *(short T1, XXXXXXXXXXXXXX T2) => T1.ToDouble() * T2; -// public static XXXXXXXXXXXXXX operator *(short? T1, XXXXXXXXXXXXXX T2) => T1.ToDouble() * T2; -// public static XXXXXXXXXXXXXX operator /(XXXXXXXXXXXXXX T1, short T2) => T1 / T2.ToDouble(); -// public static XXXXXXXXXXXXXX operator /(XXXXXXXXXXXXXX T1, short? T2) => T1 / T2.ToDouble(); - -// // int -// public static XXXXXXXXXXXXXX operator *(XXXXXXXXXXXXXX T1, int T2) => T1 * T2.ToDouble(); -// public static XXXXXXXXXXXXXX operator *(XXXXXXXXXXXXXX T1, int? T2) => T1 * T2.ToDouble(); -// public static XXXXXXXXXXXXXX operator *(int T1, XXXXXXXXXXXXXX T2) => T1.ToDouble() * T2; -// public static XXXXXXXXXXXXXX operator *(int? T1, XXXXXXXXXXXXXX T2) => T1.ToDouble() * T2; -// public static XXXXXXXXXXXXXX operator /(XXXXXXXXXXXXXX T1, int T2) => T1 / T2.ToDouble(); -// public static XXXXXXXXXXXXXX operator /(XXXXXXXXXXXXXX T1, int? T2) => T1 / T2.ToDouble(); - -// // long -// public static XXXXXXXXXXXXXX operator *(XXXXXXXXXXXXXX T1, long T2) => T1 * T2.ToDouble(); -// public static XXXXXXXXXXXXXX operator *(XXXXXXXXXXXXXX T1, long? T2) => T1 * T2.ToDouble(); -// public static XXXXXXXXXXXXXX operator *(long T1, XXXXXXXXXXXXXX T2) => T1.ToDouble() * T2; -// public static XXXXXXXXXXXXXX operator *(long? T1, XXXXXXXXXXXXXX T2) => T1.ToDouble() * T2; -// public static XXXXXXXXXXXXXX operator /(XXXXXXXXXXXXXX T1, long T2) => T1 / T2.ToDouble(); -// public static XXXXXXXXXXXXXX operator /(XXXXXXXXXXXXXX T1, long? T2) => T1 / T2.ToDouble(); - -// // byte -// public static XXXXXXXXXXXXXX operator *(XXXXXXXXXXXXXX T1, byte T2) => T1 * T2.ToDouble(); -// public static XXXXXXXXXXXXXX operator *(XXXXXXXXXXXXXX T1, byte? T2) => T1 * T2.ToDouble(); -// public static XXXXXXXXXXXXXX operator *(byte T1, XXXXXXXXXXXXXX T2) => T1.ToDouble() * T2; -// public static XXXXXXXXXXXXXX operator *(byte? T1, XXXXXXXXXXXXXX T2) => T1.ToDouble() * T2; -// public static XXXXXXXXXXXXXX operator /(XXXXXXXXXXXXXX T1, byte T2) => T1 / T2.ToDouble(); -// public static XXXXXXXXXXXXXX operator /(XXXXXXXXXXXXXX T1, byte? T2) => T1 / T2.ToDouble(); - -// // ushort -// public static XXXXXXXXXXXXXX operator *(XXXXXXXXXXXXXX T1, ushort T2) => T1 * T2.ToDouble(); -// public static XXXXXXXXXXXXXX operator *(XXXXXXXXXXXXXX T1, ushort? T2) => T1 * T2.ToDouble(); -// public static XXXXXXXXXXXXXX operator *(ushort T1, XXXXXXXXXXXXXX T2) => T1.ToDouble() * T2; -// public static XXXXXXXXXXXXXX operator *(ushort? T1, XXXXXXXXXXXXXX T2) => T1.ToDouble() * T2; -// public static XXXXXXXXXXXXXX operator /(XXXXXXXXXXXXXX T1, ushort T2) => T1 / T2.ToDouble(); -// public static XXXXXXXXXXXXXX operator /(XXXXXXXXXXXXXX T1, ushort? T2) => T1 / T2.ToDouble(); - -// // uint -// public static XXXXXXXXXXXXXX operator *(XXXXXXXXXXXXXX T1, uint T2) => T1 * T2.ToDouble(); -// public static XXXXXXXXXXXXXX operator *(XXXXXXXXXXXXXX T1, uint? T2) => T1 * T2.ToDouble(); -// public static XXXXXXXXXXXXXX operator *(uint T1, XXXXXXXXXXXXXX T2) => T1.ToDouble() * T2; -// public static XXXXXXXXXXXXXX operator *(uint? T1, XXXXXXXXXXXXXX T2) => T1.ToDouble() * T2; -// public static XXXXXXXXXXXXXX operator /(XXXXXXXXXXXXXX T1, uint T2) => T1 / T2.ToDouble(); -// public static XXXXXXXXXXXXXX operator /(XXXXXXXXXXXXXX T1, uint? T2) => T1 / T2.ToDouble(); - -// // ulong -// public static XXXXXXXXXXXXXX operator *(XXXXXXXXXXXXXX T1, ulong T2) => T1 * T2.ToDouble(); -// public static XXXXXXXXXXXXXX operator *(XXXXXXXXXXXXXX T1, ulong? T2) => T1 * T2.ToDouble(); -// public static XXXXXXXXXXXXXX operator *(ulong T1, XXXXXXXXXXXXXX T2) => T1.ToDouble() * T2; -// public static XXXXXXXXXXXXXX operator *(ulong? T1, XXXXXXXXXXXXXX T2) => T1.ToDouble() * T2; -// public static XXXXXXXXXXXXXX operator /(XXXXXXXXXXXXXX T1, ulong T2) => T1 / T2.ToDouble(); -// public static XXXXXXXXXXXXXX operator /(XXXXXXXXXXXXXX T1, ulong? T2) => T1 / T2.ToDouble(); - -// // nint -// public static XXXXXXXXXXXXXX operator *(XXXXXXXXXXXXXX T1, nint T2) => T1 * T2.ToDouble(); -// public static XXXXXXXXXXXXXX operator *(XXXXXXXXXXXXXX T1, nint? T2) => T1 * T2.ToDouble(); -// public static XXXXXXXXXXXXXX operator *(nint T1, XXXXXXXXXXXXXX T2) => T1.ToDouble() * T2; -// public static XXXXXXXXXXXXXX operator *(nint? T1, XXXXXXXXXXXXXX T2) => T1.ToDouble() * T2; -// public static XXXXXXXXXXXXXX operator /(XXXXXXXXXXXXXX T1, nint T2) => T1 / T2.ToDouble(); -// public static XXXXXXXXXXXXXX operator /(XXXXXXXXXXXXXX T1, nint? T2) => T1 / T2.ToDouble(); - -// // nuint -// public static XXXXXXXXXXXXXX operator *(XXXXXXXXXXXXXX T1, nuint T2) => T1 * T2.ToDouble(); -// public static XXXXXXXXXXXXXX operator *(XXXXXXXXXXXXXX T1, nuint? T2) => T1 * T2.ToDouble(); -// public static XXXXXXXXXXXXXX operator *(nuint T1, XXXXXXXXXXXXXX T2) => T1.ToDouble() * T2; -// public static XXXXXXXXXXXXXX operator *(nuint? T1, XXXXXXXXXXXXXX T2) => T1.ToDouble() * T2; -// public static XXXXXXXXXXXXXX operator /(XXXXXXXXXXXXXX T1, nuint T2) => T1 / T2.ToDouble(); -// public static XXXXXXXXXXXXXX operator /(XXXXXXXXXXXXXX T1, nuint? T2) => T1 / T2.ToDouble(); - -// // float -// public static XXXXXXXXXXXXXX operator *(XXXXXXXXXXXXXX T1, float T2) => T1 * T2.ToDouble(); -// public static XXXXXXXXXXXXXX operator *(XXXXXXXXXXXXXX T1, float? T2) => T1 * T2.ToDouble(); -// public static XXXXXXXXXXXXXX operator *(float T1, XXXXXXXXXXXXXX T2) => T1.ToDouble() * T2; -// public static XXXXXXXXXXXXXX operator *(float? T1, XXXXXXXXXXXXXX T2) => T1.ToDouble() * T2; -// public static XXXXXXXXXXXXXX operator /(XXXXXXXXXXXXXX T1, float T2) => T1 / T2.ToDouble(); -// public static XXXXXXXXXXXXXX operator /(XXXXXXXXXXXXXX T1, float? T2) => T1 / T2.ToDouble(); - -// // decimal -// public static XXXXXXXXXXXXXX operator *(XXXXXXXXXXXXXX T1, decimal T2) => T1 * T2.ToDouble(); -// public static XXXXXXXXXXXXXX operator *(XXXXXXXXXXXXXX T1, decimal? T2) => T1 * T2.ToDouble(); -// public static XXXXXXXXXXXXXX operator *(decimal T1, XXXXXXXXXXXXXX T2) => T1.ToDouble() * T2; -// public static XXXXXXXXXXXXXX operator *(decimal? T1, XXXXXXXXXXXXXX T2) => T1.ToDouble() * T2; -// public static XXXXXXXXXXXXXX operator /(XXXXXXXXXXXXXX T1, decimal T2) => T1 / T2.ToDouble(); -// public static XXXXXXXXXXXXXX operator /(XXXXXXXXXXXXXX T1, decimal? T2) => T1 / T2.ToDouble(); - -//#if NET7_0_OR_GREATER -// // Int128 -// public static XXXXXXXXXXXXXX operator *(XXXXXXXXXXXXXX T1, Int128 T2) => T1 * T2.ToDouble(); -// public static XXXXXXXXXXXXXX operator *(XXXXXXXXXXXXXX T1, Int128? T2) => T1 * T2.ToDouble(); -// public static XXXXXXXXXXXXXX operator *(Int128 T1, XXXXXXXXXXXXXX T2) => T1.ToDouble() * T2; -// public static XXXXXXXXXXXXXX operator *(Int128? T1, XXXXXXXXXXXXXX T2) => T1.ToDouble() * T2; -// public static XXXXXXXXXXXXXX operator /(XXXXXXXXXXXXXX T1, Int128 T2) => T1 / T2.ToDouble(); -// public static XXXXXXXXXXXXXX operator /(XXXXXXXXXXXXXX T1, Int128? T2) => T1 / T2.ToDouble(); - -// // UInt128 -// public static XXXXXXXXXXXXXX operator *(XXXXXXXXXXXXXX T1, UInt128 T2) => T1 * T2.ToDouble(); -// public static XXXXXXXXXXXXXX operator *(XXXXXXXXXXXXXX T1, UInt128? T2) => T1 * T2.ToDouble(); -// public static XXXXXXXXXXXXXX operator *(UInt128 T1, XXXXXXXXXXXXXX T2) => T1.ToDouble() * T2; -// public static XXXXXXXXXXXXXX operator *(UInt128? T1, XXXXXXXXXXXXXX T2) => T1.ToDouble() * T2; -// public static XXXXXXXXXXXXXX operator /(XXXXXXXXXXXXXX T1, UInt128 T2) => T1 / T2.ToDouble(); -// public static XXXXXXXXXXXXXX operator /(XXXXXXXXXXXXXX T1, UInt128? T2) => T1 / T2.ToDouble(); -//#endif - -// public static explicit operator XXXXXXXXXXXXXX(double val) => new(val); -// public static explicit operator double(XXXXXXXXXXXXXX unit) => unit._Value; - -// public XXXXXXXXXXXXXX Abs() => new(Math.Abs(_Value)); - -// /// C^2 = this^2 + B^2 C = (this^2 + B^2).Sqrt(2) -// public XXXXXXXXXXXXXX HypFromLeg(XXXXXXXXXXXXXX B) => new(Math.Sqrt(_Value * _Value + B._Value * B._Value)); - -// /// C^2 = this^2 + B^2 C = (this^2 + B^2).Sqrt(2) -// public XXXXXXXXXXXXXX HypFromLeg(XXXXXXXXXXXXXX? 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 XXXXXXXXXXXXXX LegFromHyp(XXXXXXXXXXXXXX C) => new(Math.Sqrt(C._Value * C._Value - _Value * _Value)); - -// /// C^2 = this^2 + B^2 B = (C^2 - this^2).Sqrt(2) -// public XXXXXXXXXXXXXX LegFromHyp(XXXXXXXXXXXXXX? 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 XXXXXXXXXXXXXX LegFromLeg(XXXXXXXXXXXXXX A) => new(Math.Sqrt(_Value * _Value - A._Value * A._Value)); - -// /// this^2 = A^2 + B^2 B = (this^2 - A^2).Sqrt(2) -// public XXXXXXXXXXXXXX LegFromLeg(XXXXXXXXXXXXXX? A) -// { -// double a = A is null ? 0d : A._Value._Value; -// return new(Math.Sqrt(_Value * _Value - a * a)); -// } -//} - -//public static class XXXXXXXXXXXXXXExtension -//{ -// internal static double ToDouble(this XXXXXXXXXXXXXX? unit) => unit is null ? 0d : unit._Value._Value; - -// public static XXXXXXXXXXXXXX MetricSum(this IEnumerable units) => new() { _Value = units?.Sum(m => m._Value) ?? 0d }; -// public static XXXXXXXXXXXXXX MetricAverage(this IEnumerable units) => new() { _Value = units?.Average(m => m._Value) ?? double.NaN }; -// public static XXXXXXXXXXXXXX MetricMax(this IEnumerable units) => new() { _Value = units?.Max(m => m._Value) ?? double.MinValue }; -// public static XXXXXXXXXXXXXX MetricMin(this IEnumerable units) => new() { _Value = units?.Min(m => m._Value) ?? double.MaxValue }; - -// public static XXXXXXXXXXXXXX MetricSum(this IEnumerable units) => new() { _Value = units?.Sum(m => m.ToDouble()) ?? 0d }; -// public static XXXXXXXXXXXXXX MetricAverage(this IEnumerable units) => new() { _Value = units?.Average(m => m.ToDouble()) ?? double.NaN }; -// public static XXXXXXXXXXXXXX MetricMax(this IEnumerable units) => new() { _Value = units?.Max(m => m.ToDouble()) ?? double.MinValue }; -// public static XXXXXXXXXXXXXX MetricMin(this IEnumerable units) => new() { _Value = units?.Min(m => m.ToDouble()) ?? double.MaxValue }; - - -// internal static void MultiplyCore(ReadOnlySpan source, double value, Span destination) -// { -// int len = source.Length; -// ReadOnlySpan srcDouble = MemoryMarshal.Cast(source); -// Span dstDouble = MemoryMarshal.Cast(destination); - -// var vectorized_Value = 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 * vectorized_Value; -// 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 source, double divisor, Span destination) -// { -// // 1. Проверка на ноль -// if (divisor == 0d || double.IsNaN(divisor)) -// throw new DivideByZeroException("Делитель не может быть равен нулю."); - -// int len = source.Length; -// ReadOnlySpan srcDouble = MemoryMarshal.Cast(source); -// Span dstDouble = MemoryMarshal.Cast(destination); - -// var vectorized_Value = 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 / vectorized_Value; -// 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 source, Span destination) -// { -// int len = source.Length; -// ReadOnlySpan srcDouble = MemoryMarshal.Cast(source); -// Span dstDouble = MemoryMarshal.Cast(destination); - -// var vectorized_Value = 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 = vectorized_Value / 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 source, double summand, Span destination) -// { -// int len = source.Length; -// ReadOnlySpan srcDouble = MemoryMarshal.Cast(source); -// Span dstDouble = MemoryMarshal.Cast(destination); - -// var vectorized_Value = 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 + vectorized_Value; -// 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 source, double subtrahend, Span destination) -// { -// int len = source.Length; -// ReadOnlySpan srcDouble = MemoryMarshal.Cast(source); -// Span dstDouble = MemoryMarshal.Cast(destination); - -// var vectorized_Value = 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 - vectorized_Value; -// 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 source, Span destination) -// { -// int len = source.Length; -// ReadOnlySpan srcDouble = MemoryMarshal.Cast(source); -// Span dstDouble = MemoryMarshal.Cast(destination); - -// var vectorized_Value = 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 = vectorized_Value - 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 source, int power, Span destination) -// { -// int len = source.Length; -// ReadOnlySpan srcDouble = MemoryMarshal.Cast(source); -// Span dstDouble = MemoryMarshal.Cast(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.Min_Value, но для степеней это редчайший кейс -// } - -// 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 source, double power, Span destination) -// { -// int len = source.Length; -// if (len == 0) return; - -// fixed (double* pSrc = MemoryMarshal.Cast(source)) -// fixed (double* pDst = MemoryMarshal.Cast(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 source, Span destination) -// { -// int len = source.Length; -// ReadOnlySpan srcDouble = MemoryMarshal.Cast(source); -// Span dstDouble = MemoryMarshal.Cast(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 XXXXXXXXXXXXXX[] Multiply(this XXXXXXXXXXXXXX[] units, double multiplicator) -// { -// if (units is null) return null!; -// if (units.Length == 0) return []; - -// var result = new XXXXXXXXXXXXXX[units.Length]; -// MultiplyCore(units, multiplicator, result); -// return result; -// } -// public static XXXXXXXXXXXXXX[] Multiply(this double multiplicator, XXXXXXXXXXXXXX[] units) -// => units.Multiply(multiplicator); -// public static XXXXXXXXXXXXXX?[] Multiply(this XXXXXXXXXXXXXX?[] units, double multiplicator) -// { -// if (units is null) return null!; -// if (units.Length == 0) return []; - -// var result = (XXXXXXXXXXXXXX?[])units.Clone(); -// int len = result.Length; -// for (int i = 0; i < len; i++) -// { -// ref var item = ref result[i]; -// if (item.Has_Value) item = new XXXXXXXXXXXXXX(item._Value._Value * multiplicator); -// } -// return result; -// } -// public static XXXXXXXXXXXXXX?[] Multiply(this double multiplicator, XXXXXXXXXXXXXX?[] units) -// => units.Multiply(multiplicator); - -// // === ReadOnlySpan === -// public static Span Multiply(this ReadOnlySpan units, double multiplicator) -// { -// if (units.Length == 0) return []; -// Span result = new XXXXXXXXXXXXXX[units.Length]; -// MultiplyCore(units, multiplicator, result); -// return result; -// } -// public static Span Multiply(this double multiplicator, ReadOnlySpan units) -// => units.Multiply(multiplicator); -// public static Span Multiply(this ReadOnlySpan units, double multiplicator) -// { -// if (units.Length == 0) return []; -// Span result = new XXXXXXXXXXXXXX?[units.Length]; -// units.CopyTo(result); - -// int len = result.Length; -// for (int i = 0; i < len; i++) -// { -// ref var item = ref result[i]; -// if (item.Has_Value) item = new XXXXXXXXXXXXXX(item._Value._Value * multiplicator); -// } -// return result; -// } -// public static Span Multiply(this double multiplicator, ReadOnlySpan units) -// => units.Multiply(multiplicator); - -// // === List === -// public static List Multiply(this List units, double multiplicator) -// { -// if (units is null) return null!; -// int len = units.Count; -// if (len == 0) return []; - -// var result = new List(len); -// result.SetCountUnsafe(len); -// MultiplyCore(CollectionsMarshal.AsSpan(units), multiplicator, CollectionsMarshal.AsSpan(result)); -// return result; -// } -// public static List Multiply(this double multiplicator, List units) -// => units.Multiply(multiplicator); -// public static List Multiply(this List units, double multiplicator) -// { -// if (units is null) return null!; -// int len = units.Count; -// if (len == 0) return []; - -// var result = new List(units); -// Span dstSpan = CollectionsMarshal.AsSpan(result); -// for (int i = 0; i < len; i++) -// { -// ref var item = ref dstSpan[i]; -// if (item.Has_Value) item = new XXXXXXXXXXXXXX(item._Value._Value * multiplicator); -// } -// return result; -// } -// public static List Multiply(this double multiplicator, List units) -// => units.Multiply(multiplicator); - -// // === ICollection === -// public static Tcoll Multiply(this ICollection units, double multiplicator) -// where Tcoll : class, ICollection, new() -// { -// ArgumentNullException.ThrowIfNull(units); -// var tColl = new Tcoll(); -// if (tColl is List list) -// list.Capacity = units.Count; -// foreach (var item in units) -// tColl.Add(new XXXXXXXXXXXXXX(item._Value * multiplicator)); -// return tColl; -// } -// public static Tcoll Multiply(this double multiplicator, ICollection units) -// where Tcoll : class, ICollection, new() => Multiply(units, multiplicator); -// public static Tcoll Multiply(this ICollection units, double multiplicator) -// where Tcoll : class, ICollection, new() -// { -// ArgumentNullException.ThrowIfNull(units); -// var tColl = new Tcoll(); -// if (tColl is List list) -// list.Capacity = units.Count; -// foreach (var item in units) -// tColl.Add(new XXXXXXXXXXXXXX(item.Has_Value ? item._Value._Value * multiplicator : 0d)); -// return tColl; -// } -// public static Tcoll Multiply(this double multiplicator, ICollection units) -// where Tcoll : class, ICollection, new() => Multiply(units, multiplicator); - -// // === IEnumerable === -// public static IEnumerable Multiply(this IEnumerable units, double multiplicator) -// { -// ArgumentNullException.ThrowIfNull(units); -// foreach (var item in units) -// yield return new XXXXXXXXXXXXXX(item._Value * multiplicator); -// } -// public static IEnumerable Multiply(this double multiplicator, IEnumerable units) -// => units.Multiply(multiplicator); -// public static IEnumerable Multiply(this IEnumerable units, double multiplicator) -// { -// ArgumentNullException.ThrowIfNull(units); -// foreach (var item in units) -// yield return item.Has_Value -// ? new XXXXXXXXXXXXXX(item._Value._Value * multiplicator) : null; -// } -// public static IEnumerable Multiply(this double multiplicator, IEnumerable units) -// => units.Multiply(multiplicator); - -// // ========================================== -// // === DIVIDE === -// // ========================================== - -// public static XXXXXXXXXXXXXX[] Divide(this XXXXXXXXXXXXXX[] 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 XXXXXXXXXXXXXX[units.Length]; -// DivideCore(units, divisor, result); -// return result; -// } -// public static XXXXXXXXXXXXXX[] Divide(this double dividend, XXXXXXXXXXXXXX[] units) -// { -// if (units is null) return null!; -// if (units.Length == 0) return []; - -// var result = new XXXXXXXXXXXXXX[units.Length]; -// DivideCore(dividend, units, result); // Вызов зеркального ядра (число / массив) -// return result; -// } -// public static XXXXXXXXXXXXXX?[] Divide(this XXXXXXXXXXXXXX?[] 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 = (XXXXXXXXXXXXXX?[])units.Clone(); -// int len = result.Length; -// for (int i = 0; i < len; i++) -// { -// ref var item = ref result[i]; -// if (item.Has_Value) item = new XXXXXXXXXXXXXX(item._Value._Value / divisor); -// } -// return result; -// } -// public static XXXXXXXXXXXXXX?[] Divide(this double dividend, XXXXXXXXXXXXXX?[] units) -// { -// if (units is null) return null!; -// if (units.Length == 0) return []; - -// var result = (XXXXXXXXXXXXXX?[])units.Clone(); -// int len = result.Length; -// for (int i = 0; i < len; i++) -// { -// ref var item = ref result[i]; -// if (item.Has_Value) item = new XXXXXXXXXXXXXX(dividend / item._Value._Value); -// } -// return result; -// } - -// // === ReadOnlySpan === -// public static Span Divide(this ReadOnlySpan units, double divisor) -// { -// if (units.Length == 0) return []; -// if (divisor == 0 || double.IsNaN(divisor)) throw new ArgumentException("Делитель не может быть 0 или NaN.", nameof(divisor)); -// var result = new XXXXXXXXXXXXXX[units.Length]; -// DivideCore(units, divisor, result); -// return result; -// } -// public static Span Divide(this double dividend, ReadOnlySpan units) -// { -// if (units.Length == 0) return []; -// var result = new XXXXXXXXXXXXXX[units.Length]; -// DivideCore(dividend, units, result); -// return result; -// } -// public static Span Divide(this ReadOnlySpan units, double divisor) -// { -// if (units.Length == 0) return []; -// if (divisor == 0 || double.IsNaN(divisor)) -// throw new ArgumentException("Делитель не может быть 0 или NaN.", nameof(divisor)); - -// Span result = new XXXXXXXXXXXXXX?[units.Length]; -// units.CopyTo(result); - -// int len = result.Length; -// for (int i = 0; i < len; i++) -// { -// ref var item = ref result[i]; -// if (item.Has_Value) item = new XXXXXXXXXXXXXX(item._Value._Value / divisor); -// } -// return result; -// } -// public static Span Divide(this double dividend, ReadOnlySpan units) -// { -// if (units.Length == 0) return []; -// Span result = new XXXXXXXXXXXXXX?[units.Length]; -// units.CopyTo(result); - -// int len = result.Length; -// for (int i = 0; i < len; i++) -// { -// ref var item = ref result[i]; -// if (item.Has_Value) item = new XXXXXXXXXXXXXX(dividend / item._Value._Value); -// } -// return result; -// } - -// // === List === -// public static List Divide(this List 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(len); -// result.SetCountUnsafe(len); -// DivideCore(CollectionsMarshal.AsSpan(units), divisor, CollectionsMarshal.AsSpan(result)); -// return result; -// } -// public static List Divide(this double dividend, List units) -// { -// if (units is null) return null!; -// int len = units.Count; -// if (len == 0) return []; - -// var result = new List(len); -// result.SetCountUnsafe(len); -// DivideCore(dividend, CollectionsMarshal.AsSpan(units), CollectionsMarshal.AsSpan(result)); -// return result; -// } -// public static List Divide(this List 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(units); -// Span dstSpan = CollectionsMarshal.AsSpan(result); -// for (int i = 0; i < len; i++) -// { -// ref var item = ref dstSpan[i]; -// if (item.Has_Value) item = new XXXXXXXXXXXXXX(item._Value._Value / divisor); -// } -// return result; -// } -// public static List Divide(this double dividend, List units) -// { -// if (units is null) return null!; -// int len = units.Count; -// if (len == 0) return []; - -// var result = new List(units); -// Span dstSpan = CollectionsMarshal.AsSpan(result); -// for (int i = 0; i < len; i++) -// { -// ref var item = ref dstSpan[i]; -// if (item.Has_Value) item = new XXXXXXXXXXXXXX(dividend / item._Value._Value); -// } -// return result; -// } - -// // === ICollection === -// public static Tcoll Divide(this ICollection units, double divisor) -// where Tcoll : class, ICollection, new() -// { -// ArgumentNullException.ThrowIfNull(units); -// var tColl = new Tcoll(); -// if (tColl is List list) -// list.Capacity = units.Count; -// foreach (var item in units) -// tColl.Add(new XXXXXXXXXXXXXX(item._Value / divisor)); -// return tColl; -// } -// public static Tcoll Divide(this double dividend, ICollection units) -// where Tcoll : class, ICollection, new() -// { -// ArgumentNullException.ThrowIfNull(units); -// var tColl = new Tcoll(); -// if (tColl is List list) -// list.Capacity = units.Count; -// foreach (var item in units) -// tColl.Add(new XXXXXXXXXXXXXX(dividend / item._Value)); -// return tColl; -// } -// public static Tcoll Divide(this ICollection units, double divisor) -// where Tcoll : class, ICollection, new() -// { -// ArgumentNullException.ThrowIfNull(units); -// var tColl = new Tcoll(); -// if (tColl is List list) -// list.Capacity = units.Count; -// foreach (var item in units) -// tColl.Add(new XXXXXXXXXXXXXX(item.Has_Value ? item._Value._Value / divisor : 0d)); -// return tColl; -// } -// public static Tcoll Divide(this double dividend, ICollection units) -// where Tcoll : class, ICollection, new() -// { -// ArgumentNullException.ThrowIfNull(units); -// var tColl = new Tcoll(); -// if (tColl is List list) -// list.Capacity = units.Count; -// foreach (var item in units) -// tColl.Add(new XXXXXXXXXXXXXX(item.Has_Value ? dividend / item._Value._Value : 0d)); -// return tColl; -// } - -// // === IEnumerable === -// public static IEnumerable Divide(this IEnumerable units, double divisor) -// { -// ArgumentNullException.ThrowIfNull(units); -// foreach (var item in units) -// yield return new XXXXXXXXXXXXXX(item._Value * divisor); -// } -// public static IEnumerable Divide(this double dividend, IEnumerable units) -// { -// ArgumentNullException.ThrowIfNull(units); -// foreach (var item in units) -// yield return new XXXXXXXXXXXXXX(dividend / item._Value); -// } -// public static IEnumerable Divide(this IEnumerable units, double divisor) -// { -// ArgumentNullException.ThrowIfNull(units); -// foreach (var item in units) -// yield return item.Has_Value -// ? new XXXXXXXXXXXXXX(item._Value._Value * divisor) : null; -// } -// public static IEnumerable Divide(this double dividend, IEnumerable units) -// { -// ArgumentNullException.ThrowIfNull(units); -// foreach (var item in units) -// yield return item.Has_Value -// ? new XXXXXXXXXXXXXX(dividend / item._Value._Value) : null; -// } - -// // ========================================== -// // === PLUS === -// // ========================================== - -// public static XXXXXXXXXXXXXX[] Plus(this XXXXXXXXXXXXXX[] units, double summand) -// { -// if (units is null) return null!; -// if (units.Length == 0) return []; - -// var result = new XXXXXXXXXXXXXX[units.Length]; -// PlusCore(units, summand, result); -// return result; -// } -// public static XXXXXXXXXXXXXX[] Plus(this double summand, XXXXXXXXXXXXXX[] units) -// => units.Plus(summand); -// public static XXXXXXXXXXXXXX?[] Plus(this XXXXXXXXXXXXXX?[] units, double summand) -// { -// if (units is null) return null!; -// if (units.Length == 0) return []; - -// var result = (XXXXXXXXXXXXXX?[])units.Clone(); -// int len = result.Length; -// for (int i = 0; i < len; i++) -// { -// ref var item = ref result[i]; -// if (item.Has_Value) item = new XXXXXXXXXXXXXX(item._Value._Value + summand); -// } -// return result; -// } -// public static XXXXXXXXXXXXXX?[] Plus(this double summand, XXXXXXXXXXXXXX?[] units) -// => units.Plus(summand); - -// // === ReadOnlySpan === -// public static Span Plus(this ReadOnlySpan units, double summand) -// { -// if (units.Length == 0) return []; -// var result = new XXXXXXXXXXXXXX[units.Length]; -// PlusCore(units, summand, result); -// return result; -// } -// public static Span Plus(this double summand, ReadOnlySpan units) -// => units.Plus(summand); -// public static Span Plus(this ReadOnlySpan units, double summand) -// { -// if (units.Length == 0) return []; -// Span result = new XXXXXXXXXXXXXX?[units.Length]; -// units.CopyTo(result); - -// int len = result.Length; -// for (int i = 0; i < len; i++) -// { -// ref var item = ref result[i]; -// if (item.Has_Value) item = new XXXXXXXXXXXXXX(item._Value._Value + summand); -// } -// return result; -// } -// public static Span Plus(this double summand, ReadOnlySpan units) -// => units.Multiply(summand); - -// // === List === -// public static List Plus(this List units, double summand) -// { -// if (units is null) return null!; -// int len = units.Count; -// if (len == 0) return []; - -// var result = new List(len); -// result.SetCountUnsafe(len); -// PlusCore(CollectionsMarshal.AsSpan(units), summand, CollectionsMarshal.AsSpan(result)); -// return result; -// } -// public static List Plus(this double summand, List units) -// => units.Plus(summand); -// public static List Plus(this List units, double summand) -// { -// if (units is null) return null!; -// int len = units.Count; -// if (len == 0) return []; - -// var result = new List(units); -// Span dstSpan = CollectionsMarshal.AsSpan(result); -// for (int i = 0; i < len; i++) -// { -// ref var item = ref dstSpan[i]; -// if (item.Has_Value) item = new XXXXXXXXXXXXXX(item._Value._Value + summand); -// } -// return result; -// } -// public static List Plus(this double summand, List units) -// => units.Plus(summand); - -// // === ICollection === -// public static Tcoll Plus(this ICollection units, double summand) -// where Tcoll : class, ICollection, new() -// { -// ArgumentNullException.ThrowIfNull(units); -// var tColl = new Tcoll(); -// if (tColl is List list) -// list.Capacity = units.Count; -// foreach (var item in units) -// tColl.Add(new XXXXXXXXXXXXXX(item._Value + summand)); -// return tColl; -// } -// public static Tcoll Plus(this double summand, ICollection units) -// where Tcoll : class, ICollection, new() => Plus(units, summand); -// public static Tcoll Plus(this ICollection units, double summand) -// where Tcoll : class, ICollection, new() -// { -// ArgumentNullException.ThrowIfNull(units); -// var tColl = new Tcoll(); -// if (tColl is List list) -// list.Capacity = units.Count; -// foreach (var item in units) -// tColl.Add(new XXXXXXXXXXXXXX(item.Has_Value ? item._Value._Value + summand : 0d)); -// return tColl; -// } -// public static Tcoll Plus(this double summand, ICollection units) -// where Tcoll : class, ICollection, new() => Plus(units, summand); - -// // === IEnumerable === -// public static IEnumerable Plus(this IEnumerable units, double summand) -// { -// ArgumentNullException.ThrowIfNull(units); -// foreach (var item in units) -// yield return new XXXXXXXXXXXXXX(item._Value + summand); -// } -// public static IEnumerable Plus(this double summand, IEnumerable units) -// => units.Plus(summand); -// public static IEnumerable Plus(this IEnumerable units, double summand) -// { -// ArgumentNullException.ThrowIfNull(units); -// foreach (var item in units) -// yield return item.Has_Value -// ? new XXXXXXXXXXXXXX(item._Value._Value + summand) : null; -// } -// public static IEnumerable Plus(this double summand, IEnumerable units) -// => units.Plus(summand); - -// // ========================================== -// // === MINUS === -// // ========================================== - -// public static XXXXXXXXXXXXXX[] Minus(this XXXXXXXXXXXXXX[] units, double subtrahend) -// { -// if (units is null) return null!; -// if (units.Length == 0) return []; - -// var result = new XXXXXXXXXXXXXX[units.Length]; -// MinusCore(units, subtrahend, result); -// return result; -// } -// public static XXXXXXXXXXXXXX[] Minus(this double minuend, XXXXXXXXXXXXXX[] units) -// { -// if (units is null) return null!; -// if (units.Length == 0) return []; - -// var result = new XXXXXXXXXXXXXX[units.Length]; -// MinusCore(minuend, units, result); // Вызов зеркального ядра (число - массив) -// return result; -// } -// public static XXXXXXXXXXXXXX?[] Minus(this XXXXXXXXXXXXXX?[] units, double subtrahend) -// { -// if (units is null) return null!; -// if (units.Length == 0) return []; - -// var result = (XXXXXXXXXXXXXX?[])units.Clone(); -// int len = result.Length; -// for (int i = 0; i < len; i++) -// { -// ref var item = ref result[i]; -// if (item.Has_Value) item = new XXXXXXXXXXXXXX(item._Value._Value - subtrahend); -// } -// return result; -// } -// public static XXXXXXXXXXXXXX?[] Minus(this double minuend, XXXXXXXXXXXXXX?[] units) -// { -// if (units is null) return null!; -// if (units.Length == 0) return []; - -// var result = (XXXXXXXXXXXXXX?[])units.Clone(); -// int len = result.Length; -// for (int i = 0; i < len; i++) -// { -// ref var item = ref result[i]; -// if (item.Has_Value) item = new XXXXXXXXXXXXXX(minuend - item._Value._Value); -// } -// return result; -// } - -// // === ReadOnlySpan === -// public static Span Minus(this ReadOnlySpan units, double subtrahend) -// { -// if (units.Length == 0) return []; -// var result = new XXXXXXXXXXXXXX[units.Length]; -// MinusCore(units, subtrahend, result); -// return result; -// } -// public static Span Minus(this double minuend, ReadOnlySpan units) -// { -// if (units.Length == 0) return []; -// var result = new XXXXXXXXXXXXXX[units.Length]; -// MinusCore(minuend, units, result); -// return result; -// } -// public static Span Minus(this ReadOnlySpan units, double subtrahend) -// { -// if (units.Length == 0) return []; -// Span result = new XXXXXXXXXXXXXX?[units.Length]; -// units.CopyTo(result); - -// int len = result.Length; -// for (int i = 0; i < len; i++) -// { -// ref var item = ref result[i]; -// if (item.Has_Value) item = new XXXXXXXXXXXXXX(item._Value._Value - subtrahend); -// } -// return result; -// } -// public static Span Minus(this double minuend, ReadOnlySpan units) -// { -// if (units.Length == 0) return []; -// Span result = new XXXXXXXXXXXXXX?[units.Length]; -// units.CopyTo(result); - -// int len = result.Length; -// for (int i = 0; i < len; i++) -// { -// ref var item = ref result[i]; -// if (item.Has_Value) item = new XXXXXXXXXXXXXX(minuend - item._Value._Value); -// } -// return result; -// } - -// // === List === -// public static List Minus(this List units, double subtrahend) -// { -// if (units is null) return null!; -// int len = units.Count; -// if (len == 0) return []; - -// var result = new List(len); -// result.SetCountUnsafe(len); -// MinusCore(CollectionsMarshal.AsSpan(units), subtrahend, CollectionsMarshal.AsSpan(result)); -// return result; -// } -// public static List Minus(this double minuend, List units) -// { -// if (units is null) return null!; -// int len = units.Count; -// if (len == 0) return []; - -// var result = new List(len); -// result.SetCountUnsafe(len); -// MinusCore(minuend, CollectionsMarshal.AsSpan(units), CollectionsMarshal.AsSpan(result)); -// return result; -// } -// public static List Minus(this List units, double subtrahend) -// { -// if (units is null) return null!; -// int len = units.Count; -// if (len == 0) return []; - -// var result = new List(units); -// Span dstSpan = CollectionsMarshal.AsSpan(result); -// for (int i = 0; i < len; i++) -// { -// ref var item = ref dstSpan[i]; -// if (item.Has_Value) item = new XXXXXXXXXXXXXX(item._Value._Value - subtrahend); -// } -// return result; -// } -// public static List Minus(this double minuend, List units) -// { -// if (units is null) return null!; -// int len = units.Count; -// if (len == 0) return []; - -// var result = new List(units); -// Span dstSpan = CollectionsMarshal.AsSpan(result); -// for (int i = 0; i < len; i++) -// { -// ref var item = ref dstSpan[i]; -// if (item.Has_Value) item = new XXXXXXXXXXXXXX(minuend - item._Value._Value); -// } -// return result; -// } - -// // === ICollection === -// public static Tcoll Minus(this ICollection units, double subtrahend) -// where Tcoll : class, ICollection, new() -// { -// ArgumentNullException.ThrowIfNull(units); -// var tColl = new Tcoll(); -// if (tColl is List list) -// list.Capacity = units.Count; -// foreach (var item in units) -// tColl.Add(new XXXXXXXXXXXXXX(item._Value - subtrahend)); -// return tColl; -// } -// public static Tcoll Minus(this double minuend, ICollection units) -// where Tcoll : class, ICollection, new() -// { -// ArgumentNullException.ThrowIfNull(units); -// var tColl = new Tcoll(); -// if (tColl is List list) -// list.Capacity = units.Count; -// foreach (var item in units) -// tColl.Add(new XXXXXXXXXXXXXX(minuend - item._Value)); -// return tColl; -// } -// public static Tcoll Minus(this ICollection units, double subtrahend) -// where Tcoll : class, ICollection, new() -// { -// ArgumentNullException.ThrowIfNull(units); -// var tColl = new Tcoll(); -// if (tColl is List list) -// list.Capacity = units.Count; -// foreach (var item in units) -// tColl.Add(new XXXXXXXXXXXXXX(item.Has_Value ? item._Value._Value - subtrahend : 0d)); -// return tColl; -// } -// public static Tcoll Minus(this double minuend, ICollection units) -// where Tcoll : class, ICollection, new() -// { -// ArgumentNullException.ThrowIfNull(units); -// var tColl = new Tcoll(); -// if (tColl is List list) -// list.Capacity = units.Count; -// foreach (var item in units) -// tColl.Add(new XXXXXXXXXXXXXX(item.Has_Value ? minuend - item._Value._Value : 0d)); -// return tColl; -// } - -// // === IEnumerable === -// public static IEnumerable Minus(this IEnumerable units, double subtrahend) -// { -// ArgumentNullException.ThrowIfNull(units); -// foreach (var item in units) -// yield return new XXXXXXXXXXXXXX(item._Value - subtrahend); -// } -// public static IEnumerable Minus(this double minuend, IEnumerable units) -// { -// ArgumentNullException.ThrowIfNull(units); -// foreach (var item in units) -// yield return new XXXXXXXXXXXXXX(minuend - item._Value); -// } -// public static IEnumerable Minus(this IEnumerable units, double subtrahend) -// { -// ArgumentNullException.ThrowIfNull(units); -// foreach (var item in units) -// yield return item.Has_Value -// ? new XXXXXXXXXXXXXX(item._Value._Value - subtrahend) : null; -// } -// public static IEnumerable Minus(this double minuend, IEnumerable units) -// { -// ArgumentNullException.ThrowIfNull(units); -// foreach (var item in units) -// yield return item.Has_Value -// ? new XXXXXXXXXXXXXX(minuend - item._Value._Value) : null; -// } - -// // ========================================== -// // ЦЕЛАЯ СТЕПЕНЬ (int power) -// // ========================================== - -// public static XXXXXXXXXXXXXX[] Pow(this XXXXXXXXXXXXXX[] units, int power) -// { -// if (units is null) return null!; -// if (units.Length == 0) return []; - -// var result = new XXXXXXXXXXXXXX[units.Length]; -// PowCore(units, power, result); -// return result; -// } -// public static XXXXXXXXXXXXXX?[] Pow(this XXXXXXXXXXXXXX?[] units, int power) -// { -// if (units is null) return null!; -// if (units.Length == 0) return []; - -// var result = (XXXXXXXXXXXXXX?[])units.Clone(); -// int len = result.Length; -// for (int i = 0; i < len; i++) -// { -// ref var item = ref result[i]; -// if (item.Has_Value) -// { -// // Используем тот же быстрый QuickPow, что и в ядре, чтобы не вызывать тяжелый Math.Pow -// item = new XXXXXXXXXXXXXX(item._Value._Value.QuickPow(power)); -// } -// } -// return result; -// } - -// // === ReadOnlySpan === -// public static Span Pow(this ReadOnlySpan units, int power) -// { -// if (units.Length == 0) return []; -// var result = new XXXXXXXXXXXXXX[units.Length]; -// PowCore(units, power, result); -// return result; -// } -// public static Span Pow(this ReadOnlySpan units, int power) -// { -// int len = units.Length; -// if (len == 0) return []; -// Span result = new XXXXXXXXXXXXXX?[units.Length]; -// units.CopyTo(result); -// for (int i = 0; i < len; i++) -// { -// ref var item = ref result[i]; -// if (item.Has_Value) item = new XXXXXXXXXXXXXX(item._Value._Value.QuickPow(power)); -// } -// return result; -// } - -// // === List === -// public static List Pow(this List units, int power) -// { -// if (units is null) return null!; -// int len = units.Count; -// if (len == 0) return []; - -// var result = new List(len); -// result.SetCountUnsafe(len); -// PowCore(CollectionsMarshal.AsSpan(units), power, CollectionsMarshal.AsSpan(result)); -// return result; -// } -// public static List Pow(this List units, int power) -// { -// if (units is null) return null!; -// int len = units.Count; -// if (len == 0) return []; - -// var result = new List(units); -// Span dstSpan = CollectionsMarshal.AsSpan(result); -// for (int i = 0; i < len; i++) -// { -// ref var item = ref dstSpan[i]; -// if (item.Has_Value) item = new XXXXXXXXXXXXXX(item._Value._Value.QuickPow(power)); -// } -// return result; -// } - -// // === ICollection === -// public static Tcoll Pow(this ICollection units, int power) -// where Tcoll : class, ICollection, new() -// { -// ArgumentNullException.ThrowIfNull(units); -// var tColl = new Tcoll(); -// if (tColl is List list) -// list.Capacity = units.Count; -// foreach (var item in units) -// tColl.Add(new XXXXXXXXXXXXXX(item._Value.QuickPow(power))); -// return tColl; -// } -// public static Tcoll Pow(this ICollection units, int power) -// where Tcoll : class, ICollection, new() -// { -// ArgumentNullException.ThrowIfNull(units); -// var tColl = new Tcoll(); -// if (tColl is List list) -// list.Capacity = units.Count; -// foreach (var item in units) -// tColl.Add(new XXXXXXXXXXXXXX(item.Has_Value ? item._Value._Value.QuickPow(power) : 0d)); -// return tColl; -// } - -// // === IEnumerable === -// public static IEnumerable Pow(this IEnumerable units, int power) -// { -// ArgumentNullException.ThrowIfNull(units); -// foreach (var item in units) -// yield return new XXXXXXXXXXXXXX(item._Value.QuickPow(power)); -// } -// public static IEnumerable Pow(this IEnumerable units, int power) -// { -// ArgumentNullException.ThrowIfNull(units); -// foreach (var item in units) -// yield return item.Has_Value -// ? new XXXXXXXXXXXXXX(item._Value._Value.QuickPow(power)) : null; -// } - -// // ========================================== -// // ДРОБНАЯ СТЕПЕНЬ (double power) -// // ========================================== - -// public static XXXXXXXXXXXXXX[] Pow(this XXXXXXXXXXXXXX[] units, double power) -// { -// if (units is null) return null!; -// if (units.Length == 0) return []; - -// var result = new XXXXXXXXXXXXXX[units.Length]; -// PowCore(units, power, result); -// return result; -// } -// public static XXXXXXXXXXXXXX?[] Pow(this XXXXXXXXXXXXXX?[] units, double power) -// { -// if (units is null) return null!; -// if (units.Length == 0) return []; - -// var result = (XXXXXXXXXXXXXX?[])units.Clone(); -// int len = result.Length; -// for (int i = 0; i < len; i++) -// { -// ref var item = ref result[i]; -// if (item.Has_Value) -// { -// item = new XXXXXXXXXXXXXX(Math.Pow(item._Value._Value, power)); -// } -// } -// return result; -// } - -// // === ReadOnlySpan === -// public static Span Pow(this ReadOnlySpan units, double power) -// { -// if (units.Length == 0) return []; -// var result = new XXXXXXXXXXXXXX[units.Length]; -// PowCore(units, power, result); -// return result; -// } -// public static Span Pow(this ReadOnlySpan units, double power) -// { -// int len = units.Length; -// if (len == 0) return []; -// Span result = new XXXXXXXXXXXXXX?[units.Length]; -// units.CopyTo(result); -// for (int i = 0; i < len; i++) -// { -// ref var item = ref result[i]; -// if (item.Has_Value) item = new XXXXXXXXXXXXXX(Math.Pow(item._Value._Value, power)); -// } -// return result; -// } - -// // === List === -// public static List Pow(this List units, double power) -// { -// if (units is null) return null!; -// int len = units.Count; -// if (len == 0) return []; - -// var result = new List(len); -// result.SetCountUnsafe(len); -// PowCore(CollectionsMarshal.AsSpan(units), power, CollectionsMarshal.AsSpan(result)); -// return result; -// } -// public static List Pow(this List units, double power) -// { -// if (units is null) return null!; -// int len = units.Count; -// if (len == 0) return []; - -// var result = new List(units); -// Span dstSpan = CollectionsMarshal.AsSpan(result); -// for (int i = 0; i < len; i++) -// { -// ref var item = ref dstSpan[i]; -// if (item.Has_Value) item = new XXXXXXXXXXXXXX(Math.Pow(item._Value._Value, power)); -// } -// return result; -// } - -// // === ICollection === -// public static Tcoll Pow(this ICollection units, double power) -// where Tcoll : class, ICollection, new() -// { -// ArgumentNullException.ThrowIfNull(units); -// var tColl = new Tcoll(); -// if (tColl is List list) -// list.Capacity = units.Count; -// foreach (var item in units) -// tColl.Add(new XXXXXXXXXXXXXX(Math.Pow(item._Value, power))); -// return tColl; -// } -// public static Tcoll Pow(this ICollection units, double power) -// where Tcoll : class, ICollection, new() -// { -// ArgumentNullException.ThrowIfNull(units); -// var tColl = new Tcoll(); -// if (tColl is List list) -// list.Capacity = units.Count; -// foreach (var item in units) -// tColl.Add(new XXXXXXXXXXXXXX(item.Has_Value ? Math.Pow(item._Value._Value, power) : 0d)); -// return tColl; -// } - -// // === IEnumerable === -// public static IEnumerable Pow(this IEnumerable units, double power) -// { -// ArgumentNullException.ThrowIfNull(units); -// foreach (var item in units) -// yield return new XXXXXXXXXXXXXX(Math.Pow(item._Value, power)); -// } -// public static IEnumerable Pow(this IEnumerable units, double power) -// { -// ArgumentNullException.ThrowIfNull(units); -// foreach (var item in units) -// yield return item.Has_Value -// ? new XXXXXXXXXXXXXX(Math.Pow(item._Value._Value, power)) : null; -// } - - -// // ========================================== -// // КВАДРАТНЫЙ КОРЕНЬ (Sqrt) -// // ========================================== - -// public static XXXXXXXXXXXXXX[] Sqrt(this XXXXXXXXXXXXXX[] units) -// { -// if (units is null) return null!; -// if (units.Length == 0) return []; - -// var result = new XXXXXXXXXXXXXX[units.Length]; -// SqrtCore(units, result); -// return result; -// } -// public static XXXXXXXXXXXXXX?[] Sqrt(this XXXXXXXXXXXXXX?[] units) -// { -// if (units is null) return null!; -// if (units.Length == 0) return []; - -// var result = (XXXXXXXXXXXXXX?[])units.Clone(); -// int len = result.Length; -// for (int i = 0; i < len; i++) -// { -// ref var item = ref result[i]; -// if (item.Has_Value) -// { -// item = new XXXXXXXXXXXXXX(Math.Sqrt(item._Value._Value)); -// } -// } -// return result; -// } - -// // === ReadOnlySpan === -// public static Span Sqrt(this ReadOnlySpan units) -// { -// if (units.Length == 0) return []; -// var result = new XXXXXXXXXXXXXX[units.Length]; -// SqrtCore(units, result); -// return result; -// } -// public static Span Sqrt(this ReadOnlySpan units) -// { -// int len = units.Length; -// if (len == 0) return []; -// Span result = new XXXXXXXXXXXXXX?[units.Length]; -// units.CopyTo(result); -// for (int i = 0; i < len; i++) -// { -// ref var item = ref result[i]; -// if (item.Has_Value) item = new XXXXXXXXXXXXXX(Math.Sqrt(item._Value._Value)); -// } -// return result; -// } - -// // === List === -// public static List Sqrt(this List units) -// { -// if (units is null) return null!; -// int len = units.Count; -// if (len == 0) return []; - -// var result = new List(len); -// result.SetCountUnsafe(len); -// SqrtCore(CollectionsMarshal.AsSpan(units), CollectionsMarshal.AsSpan(result)); -// return result; -// } -// public static List Sqrt(this List units) -// { -// if (units is null) return null!; -// int len = units.Count; -// if (len == 0) return []; - -// var result = new List(units); -// Span dstSpan = CollectionsMarshal.AsSpan(result); -// for (int i = 0; i < len; i++) -// { -// ref var item = ref dstSpan[i]; -// if (item.Has_Value) item = new XXXXXXXXXXXXXX(Math.Sqrt(item._Value._Value)); -// } -// return result; -// } - -// // === ICollection === -// public static Tcoll Pow(this ICollection units) -// where Tcoll : class, ICollection, new() -// { -// ArgumentNullException.ThrowIfNull(units); -// var tColl = new Tcoll(); -// if (tColl is List list) -// list.Capacity = units.Count; -// foreach (var item in units) -// tColl.Add(new XXXXXXXXXXXXXX(Math.Sqrt(item._Value))); -// return tColl; -// } -// public static Tcoll Pow(this ICollection units) -// where Tcoll : class, ICollection, new() -// { -// ArgumentNullException.ThrowIfNull(units); -// var tColl = new Tcoll(); -// if (tColl is List list) -// list.Capacity = units.Count; -// foreach (var item in units) -// tColl.Add(new XXXXXXXXXXXXXX(item.Has_Value ? Math.Sqrt(item._Value._Value) : 0d)); -// return tColl; -// } - -// // === IEnumerable === -// public static IEnumerable Pow(this IEnumerable units) -// { -// ArgumentNullException.ThrowIfNull(units); -// foreach (var item in units) -// yield return new XXXXXXXXXXXXXX(Math.Sqrt(item._Value)); -// } -// public static IEnumerable Pow(this IEnumerable units) -// { -// ArgumentNullException.ThrowIfNull(units); -// foreach (var item in units) -// yield return item.Has_Value -// ? new XXXXXXXXXXXXXX(Math.Sqrt(item._Value._Value)) : null; -// } -//} - -//public class XXXXXXXXXXXXXXConverter : JsonConverter -//{ -// // Используем инвариантную культуру, чтобы разделителем всегда была точка (10.5, а не 10,5) -// private static readonly CultureInfo Culture = CultureInfo.InvariantCulture; - -// public override XXXXXXXXXXXXXX Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) -// { -// double double_Value; - -// if (reader.TokenType == JsonTokenType.String) -// { -// // Безопасно парсим double из строки с поддержкой точки как разделителя -// if (!double.TryParse(reader.GetString(), NumberStyles.Float, Culture, out double_Value)) -// { -// throw new JsonException($"Не удалось преобразовать строковое значение в double для метрики {nameof(XXXXXXXXXXXXXX)}."); -// } -// } -// else -// { -// // Прямое быстрое чтение числа из JSON -// double_Value = reader.GetDouble(); -// } - -// return new(double_Value); -// } - -// public override void Write(Utf8JsonWriter writer, XXXXXXXXXXXXXX value, JsonSerializerOptions options) -// { -// // Записываем число напрямую в байтовый буфер без выделения памяти под строки -// writer.WriteNumberValue(value._Value); -// } - -// public override void WriteAsPropertyName(Utf8JsonWriter writer, XXXXXXXXXXXXXX value, JsonSerializerOptions options) -// { -// // Ключи JSON-объектов всегда должны быть строками. -// // Форматируем double в строку с точкой, чтобы другие сервисы экосистемы прочитали её корректно. -// // Формат "R" (Round-trip) гарантирует, что число не потеряет точность при обратном парсинге. -// writer.WritePropertyName(value._Value.ToString("R", Culture)); -// } - -// public override XXXXXXXXXXXXXX ReadAsPropertyName(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) -// { -// string propertyName = reader.GetString()!; - -// if (!double.TryParse(propertyName, NumberStyles.Float, Culture, out double double_Value)) -// { -// throw new JsonException($"Невалидное числовое значение в ключе свойства JSON: '{propertyName}' для метрики {nameof(XXXXXXXXXXXXXX)}."); -// } - -// return new(double_Value); -// } -//} \ No newline at end of file +#if DEBUG +global using XXXXXXXXXXXXXX = QWERTYkez.Mensura.Units.XXXXXXXXXXXXXX; +global using XXXXXXXXXXXXXXConverter = QWERTYkez.Mensura.Units.XXXXXXXXXXXXXXConverter; +global using XXXXXXXXXXXXXXExtensions = QWERTYkez.Mensura.Units.XXXXXXXXXXXXXXExtension; +using System.Globalization; +using System.Runtime.Intrinsics; +using System.Runtime.Serialization; + +namespace QWERTYkez.Mensura.Units; + +[JsonConverter(typeof(XXXXXXXXXXXXXXConverter))] +public readonly partial record struct XXXXXXXXXXXXXX : IMensuraUnit, IEquatable, 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 XXXXXXXXXXXXXX(double value) => _Value = value; + + public override int GetHashCode() => _Value.GetHashCode(); + public int CompareTo(XXXXXXXXXXXXXX? other) => _Value.CompareTo(other is null ? 0d : other.Value._Value); + public int CompareTo(XXXXXXXXXXXXXX other) => _Value.CompareTo(other._Value); + + public bool Equals(XXXXXXXXXXXXXX? 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 XXXXXXXXXXXXXX Zero { get; } = new(0d); + public static XXXXXXXXXXXXXX Min { get; } = new(double.MinValue); + public static XXXXXXXXXXXXXX Max { get; } = new(double.MaxValue); + public static XXXXXXXXXXXXXX NegativeInfinity { get; } = new(double.NegativeInfinity); + public static XXXXXXXXXXXXXX PositiveInfinity { get; } = new(double.PositiveInfinity); + + + public static bool operator ==(XXXXXXXXXXXXXX? T1, XXXXXXXXXXXXXX? T2) => (T1 is null ? 0d : T1.Value._Value) == (T2 is null ? 0d : T2.Value._Value); + public static bool operator !=(XXXXXXXXXXXXXX? T1, XXXXXXXXXXXXXX? T2) => (T1 is null ? 0d : T1.Value._Value) != (T2 is null ? 0d : T2.Value._Value); + + public static bool operator <(XXXXXXXXXXXXXX? T1, XXXXXXXXXXXXXX? T2) => (T1 is null ? 0d : T1.Value._Value) < (T2 is null ? 0d : T2.Value._Value); + public static bool operator <=(XXXXXXXXXXXXXX? T1, XXXXXXXXXXXXXX? T2) => (T1 is null ? 0d : T1.Value._Value) <= (T2 is null ? 0d : T2.Value._Value); + public static bool operator >(XXXXXXXXXXXXXX? T1, XXXXXXXXXXXXXX? T2) => (T1 is null ? 0d : T1.Value._Value) > (T2 is null ? 0d : T2.Value._Value); + public static bool operator >=(XXXXXXXXXXXXXX? T1, XXXXXXXXXXXXXX? T2) => (T1 is null ? 0d : T1.Value._Value) >= (T2 is null ? 0d : T2.Value._Value); + + + public static XXXXXXXXXXXXXX operator +(XXXXXXXXXXXXXX T2) => new(+T2._Value); + public static XXXXXXXXXXXXXX operator +(XXXXXXXXXXXXXX T1, XXXXXXXXXXXXXX T2) => new(T1._Value + T2._Value); + public static XXXXXXXXXXXXXX operator -(XXXXXXXXXXXXXX T2) => new(-T2._Value); + public static XXXXXXXXXXXXXX operator -(XXXXXXXXXXXXXX T1, XXXXXXXXXXXXXX T2) => new(T1._Value - T2._Value); + + + // double + public static XXXXXXXXXXXXXX operator *(XXXXXXXXXXXXXX T1, double T2) => new(T1._Value * T2); + public static XXXXXXXXXXXXXX operator *(XXXXXXXXXXXXXX T1, double? T2) => T1 * (T2 ?? 0d); + public static XXXXXXXXXXXXXX operator *(double T1, XXXXXXXXXXXXXX T2) => new(T1 * T2._Value); + public static XXXXXXXXXXXXXX operator *(double? T1, XXXXXXXXXXXXXX T2) => (T1 ?? 0d) * T2; + public static XXXXXXXXXXXXXX operator /(XXXXXXXXXXXXXX T1, double T2) => new(T1._Value / T2); + public static XXXXXXXXXXXXXX operator /(XXXXXXXXXXXXXX T1, double? T2) => T1 / (T2 ?? 0d); + public static double operator /(XXXXXXXXXXXXXX T1, XXXXXXXXXXXXXX T2) => T1._Value / T2._Value; + + // sbyte + public static XXXXXXXXXXXXXX operator *(XXXXXXXXXXXXXX T1, sbyte T2) => T1 * T2.ToDouble(); + public static XXXXXXXXXXXXXX operator *(XXXXXXXXXXXXXX T1, sbyte? T2) => T1 * T2.ToDouble(); + public static XXXXXXXXXXXXXX operator *(sbyte T1, XXXXXXXXXXXXXX T2) => T1.ToDouble() * T2; + public static XXXXXXXXXXXXXX operator *(sbyte? T1, XXXXXXXXXXXXXX T2) => T1.ToDouble() * T2; + public static XXXXXXXXXXXXXX operator /(XXXXXXXXXXXXXX T1, sbyte T2) => T1 / T2.ToDouble(); + public static XXXXXXXXXXXXXX operator /(XXXXXXXXXXXXXX T1, sbyte? T2) => T1 / T2.ToDouble(); + + // short + public static XXXXXXXXXXXXXX operator *(XXXXXXXXXXXXXX T1, short T2) => T1 * T2.ToDouble(); + public static XXXXXXXXXXXXXX operator *(XXXXXXXXXXXXXX T1, short? T2) => T1 * T2.ToDouble(); + public static XXXXXXXXXXXXXX operator *(short T1, XXXXXXXXXXXXXX T2) => T1.ToDouble() * T2; + public static XXXXXXXXXXXXXX operator *(short? T1, XXXXXXXXXXXXXX T2) => T1.ToDouble() * T2; + public static XXXXXXXXXXXXXX operator /(XXXXXXXXXXXXXX T1, short T2) => T1 / T2.ToDouble(); + public static XXXXXXXXXXXXXX operator /(XXXXXXXXXXXXXX T1, short? T2) => T1 / T2.ToDouble(); + + // int + public static XXXXXXXXXXXXXX operator *(XXXXXXXXXXXXXX T1, int T2) => T1 * T2.ToDouble(); + public static XXXXXXXXXXXXXX operator *(XXXXXXXXXXXXXX T1, int? T2) => T1 * T2.ToDouble(); + public static XXXXXXXXXXXXXX operator *(int T1, XXXXXXXXXXXXXX T2) => T1.ToDouble() * T2; + public static XXXXXXXXXXXXXX operator *(int? T1, XXXXXXXXXXXXXX T2) => T1.ToDouble() * T2; + public static XXXXXXXXXXXXXX operator /(XXXXXXXXXXXXXX T1, int T2) => T1 / T2.ToDouble(); + public static XXXXXXXXXXXXXX operator /(XXXXXXXXXXXXXX T1, int? T2) => T1 / T2.ToDouble(); + + // long + public static XXXXXXXXXXXXXX operator *(XXXXXXXXXXXXXX T1, long T2) => T1 * T2.ToDouble(); + public static XXXXXXXXXXXXXX operator *(XXXXXXXXXXXXXX T1, long? T2) => T1 * T2.ToDouble(); + public static XXXXXXXXXXXXXX operator *(long T1, XXXXXXXXXXXXXX T2) => T1.ToDouble() * T2; + public static XXXXXXXXXXXXXX operator *(long? T1, XXXXXXXXXXXXXX T2) => T1.ToDouble() * T2; + public static XXXXXXXXXXXXXX operator /(XXXXXXXXXXXXXX T1, long T2) => T1 / T2.ToDouble(); + public static XXXXXXXXXXXXXX operator /(XXXXXXXXXXXXXX T1, long? T2) => T1 / T2.ToDouble(); + + // byte + public static XXXXXXXXXXXXXX operator *(XXXXXXXXXXXXXX T1, byte T2) => T1 * T2.ToDouble(); + public static XXXXXXXXXXXXXX operator *(XXXXXXXXXXXXXX T1, byte? T2) => T1 * T2.ToDouble(); + public static XXXXXXXXXXXXXX operator *(byte T1, XXXXXXXXXXXXXX T2) => T1.ToDouble() * T2; + public static XXXXXXXXXXXXXX operator *(byte? T1, XXXXXXXXXXXXXX T2) => T1.ToDouble() * T2; + public static XXXXXXXXXXXXXX operator /(XXXXXXXXXXXXXX T1, byte T2) => T1 / T2.ToDouble(); + public static XXXXXXXXXXXXXX operator /(XXXXXXXXXXXXXX T1, byte? T2) => T1 / T2.ToDouble(); + + // ushort + public static XXXXXXXXXXXXXX operator *(XXXXXXXXXXXXXX T1, ushort T2) => T1 * T2.ToDouble(); + public static XXXXXXXXXXXXXX operator *(XXXXXXXXXXXXXX T1, ushort? T2) => T1 * T2.ToDouble(); + public static XXXXXXXXXXXXXX operator *(ushort T1, XXXXXXXXXXXXXX T2) => T1.ToDouble() * T2; + public static XXXXXXXXXXXXXX operator *(ushort? T1, XXXXXXXXXXXXXX T2) => T1.ToDouble() * T2; + public static XXXXXXXXXXXXXX operator /(XXXXXXXXXXXXXX T1, ushort T2) => T1 / T2.ToDouble(); + public static XXXXXXXXXXXXXX operator /(XXXXXXXXXXXXXX T1, ushort? T2) => T1 / T2.ToDouble(); + + // uint + public static XXXXXXXXXXXXXX operator *(XXXXXXXXXXXXXX T1, uint T2) => T1 * T2.ToDouble(); + public static XXXXXXXXXXXXXX operator *(XXXXXXXXXXXXXX T1, uint? T2) => T1 * T2.ToDouble(); + public static XXXXXXXXXXXXXX operator *(uint T1, XXXXXXXXXXXXXX T2) => T1.ToDouble() * T2; + public static XXXXXXXXXXXXXX operator *(uint? T1, XXXXXXXXXXXXXX T2) => T1.ToDouble() * T2; + public static XXXXXXXXXXXXXX operator /(XXXXXXXXXXXXXX T1, uint T2) => T1 / T2.ToDouble(); + public static XXXXXXXXXXXXXX operator /(XXXXXXXXXXXXXX T1, uint? T2) => T1 / T2.ToDouble(); + + // ulong + public static XXXXXXXXXXXXXX operator *(XXXXXXXXXXXXXX T1, ulong T2) => T1 * T2.ToDouble(); + public static XXXXXXXXXXXXXX operator *(XXXXXXXXXXXXXX T1, ulong? T2) => T1 * T2.ToDouble(); + public static XXXXXXXXXXXXXX operator *(ulong T1, XXXXXXXXXXXXXX T2) => T1.ToDouble() * T2; + public static XXXXXXXXXXXXXX operator *(ulong? T1, XXXXXXXXXXXXXX T2) => T1.ToDouble() * T2; + public static XXXXXXXXXXXXXX operator /(XXXXXXXXXXXXXX T1, ulong T2) => T1 / T2.ToDouble(); + public static XXXXXXXXXXXXXX operator /(XXXXXXXXXXXXXX T1, ulong? T2) => T1 / T2.ToDouble(); + + // nint + public static XXXXXXXXXXXXXX operator *(XXXXXXXXXXXXXX T1, nint T2) => T1 * T2.ToDouble(); + public static XXXXXXXXXXXXXX operator *(XXXXXXXXXXXXXX T1, nint? T2) => T1 * T2.ToDouble(); + public static XXXXXXXXXXXXXX operator *(nint T1, XXXXXXXXXXXXXX T2) => T1.ToDouble() * T2; + public static XXXXXXXXXXXXXX operator *(nint? T1, XXXXXXXXXXXXXX T2) => T1.ToDouble() * T2; + public static XXXXXXXXXXXXXX operator /(XXXXXXXXXXXXXX T1, nint T2) => T1 / T2.ToDouble(); + public static XXXXXXXXXXXXXX operator /(XXXXXXXXXXXXXX T1, nint? T2) => T1 / T2.ToDouble(); + + // nuint + public static XXXXXXXXXXXXXX operator *(XXXXXXXXXXXXXX T1, nuint T2) => T1 * T2.ToDouble(); + public static XXXXXXXXXXXXXX operator *(XXXXXXXXXXXXXX T1, nuint? T2) => T1 * T2.ToDouble(); + public static XXXXXXXXXXXXXX operator *(nuint T1, XXXXXXXXXXXXXX T2) => T1.ToDouble() * T2; + public static XXXXXXXXXXXXXX operator *(nuint? T1, XXXXXXXXXXXXXX T2) => T1.ToDouble() * T2; + public static XXXXXXXXXXXXXX operator /(XXXXXXXXXXXXXX T1, nuint T2) => T1 / T2.ToDouble(); + public static XXXXXXXXXXXXXX operator /(XXXXXXXXXXXXXX T1, nuint? T2) => T1 / T2.ToDouble(); + + // float + public static XXXXXXXXXXXXXX operator *(XXXXXXXXXXXXXX T1, float T2) => T1 * T2.ToDouble(); + public static XXXXXXXXXXXXXX operator *(XXXXXXXXXXXXXX T1, float? T2) => T1 * T2.ToDouble(); + public static XXXXXXXXXXXXXX operator *(float T1, XXXXXXXXXXXXXX T2) => T1.ToDouble() * T2; + public static XXXXXXXXXXXXXX operator *(float? T1, XXXXXXXXXXXXXX T2) => T1.ToDouble() * T2; + public static XXXXXXXXXXXXXX operator /(XXXXXXXXXXXXXX T1, float T2) => T1 / T2.ToDouble(); + public static XXXXXXXXXXXXXX operator /(XXXXXXXXXXXXXX T1, float? T2) => T1 / T2.ToDouble(); + + // decimal + public static XXXXXXXXXXXXXX operator *(XXXXXXXXXXXXXX T1, decimal T2) => T1 * T2.ToDouble(); + public static XXXXXXXXXXXXXX operator *(XXXXXXXXXXXXXX T1, decimal? T2) => T1 * T2.ToDouble(); + public static XXXXXXXXXXXXXX operator *(decimal T1, XXXXXXXXXXXXXX T2) => T1.ToDouble() * T2; + public static XXXXXXXXXXXXXX operator *(decimal? T1, XXXXXXXXXXXXXX T2) => T1.ToDouble() * T2; + public static XXXXXXXXXXXXXX operator /(XXXXXXXXXXXXXX T1, decimal T2) => T1 / T2.ToDouble(); + public static XXXXXXXXXXXXXX operator /(XXXXXXXXXXXXXX T1, decimal? T2) => T1 / T2.ToDouble(); + +#if NET7_0_OR_GREATER + // Int128 + public static XXXXXXXXXXXXXX operator *(XXXXXXXXXXXXXX T1, Int128 T2) => T1 * T2.ToDouble(); + public static XXXXXXXXXXXXXX operator *(XXXXXXXXXXXXXX T1, Int128? T2) => T1 * T2.ToDouble(); + public static XXXXXXXXXXXXXX operator *(Int128 T1, XXXXXXXXXXXXXX T2) => T1.ToDouble() * T2; + public static XXXXXXXXXXXXXX operator *(Int128? T1, XXXXXXXXXXXXXX T2) => T1.ToDouble() * T2; + public static XXXXXXXXXXXXXX operator /(XXXXXXXXXXXXXX T1, Int128 T2) => T1 / T2.ToDouble(); + public static XXXXXXXXXXXXXX operator /(XXXXXXXXXXXXXX T1, Int128? T2) => T1 / T2.ToDouble(); + + // UInt128 + public static XXXXXXXXXXXXXX operator *(XXXXXXXXXXXXXX T1, UInt128 T2) => T1 * T2.ToDouble(); + public static XXXXXXXXXXXXXX operator *(XXXXXXXXXXXXXX T1, UInt128? T2) => T1 * T2.ToDouble(); + public static XXXXXXXXXXXXXX operator *(UInt128 T1, XXXXXXXXXXXXXX T2) => T1.ToDouble() * T2; + public static XXXXXXXXXXXXXX operator *(UInt128? T1, XXXXXXXXXXXXXX T2) => T1.ToDouble() * T2; + public static XXXXXXXXXXXXXX operator /(XXXXXXXXXXXXXX T1, UInt128 T2) => T1 / T2.ToDouble(); + public static XXXXXXXXXXXXXX operator /(XXXXXXXXXXXXXX T1, UInt128? T2) => T1 / T2.ToDouble(); +#endif + + public static explicit operator XXXXXXXXXXXXXX(double val) => Unsafe.As(ref val); + public static explicit operator double(XXXXXXXXXXXXXX unit) => unit._Value; + + public XXXXXXXXXXXXXX Abs() => new(Math.Abs(_Value)); + + /// C^2 = this^2 + B^2 C = (this^2 + B^2).Sqrt(2) + public XXXXXXXXXXXXXX HypFromLeg(XXXXXXXXXXXXXX B) => new(Math.Sqrt(_Value * _Value + B._Value * B._Value)); + + /// C^2 = this^2 + B^2 C = (this^2 + B^2).Sqrt(2) + public XXXXXXXXXXXXXX HypFromLeg(XXXXXXXXXXXXXX? 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 XXXXXXXXXXXXXX LegFromHyp(XXXXXXXXXXXXXX C) => new(Math.Sqrt(C._Value * C._Value - _Value * _Value)); + + /// C^2 = this^2 + B^2 B = (C^2 - this^2).Sqrt(2) + public XXXXXXXXXXXXXX LegFromHyp(XXXXXXXXXXXXXX? 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 XXXXXXXXXXXXXX LegFromLeg(XXXXXXXXXXXXXX A) => new(Math.Sqrt(_Value * _Value - A._Value * A._Value)); + + /// this^2 = A^2 + B^2 B = (this^2 - A^2).Sqrt(2) + public XXXXXXXXXXXXXX LegFromLeg(XXXXXXXXXXXXXX? A) + { + double a = A is null ? 0d : A.Value._Value; + return new(Math.Sqrt(_Value * _Value - a * a)); + } +} + +public static class XXXXXXXXXXXXXXExtension +{ + + // ========================================== + // === MULTIPLY === + // ========================================== + + public static XXXXXXXXXXXXXX[] Multiply(this XXXXXXXXXXXXXX[] units, double multiplicator) + { + if (units is null) return null!; + if (units.Length == 0) return []; + + var result = new XXXXXXXXXXXXXX[units.Length]; + Extensions.MultiplyCore(units, multiplicator, result); + return result; + } + public static XXXXXXXXXXXXXX[] Multiply(this double multiplicator, XXXXXXXXXXXXXX[] units) + => units.Multiply(multiplicator); + public static XXXXXXXXXXXXXX?[] Multiply(this XXXXXXXXXXXXXX?[] units, double multiplicator) + { + if (units is null) return null!; + if (units.Length == 0) return []; + + var result = (XXXXXXXXXXXXXX?[])units.Clone(); + int len = result.Length; + for (int i = 0; i < len; i++) + { + ref var item = ref result[i]; + if (item.HasValue) item = new XXXXXXXXXXXXXX(item.Value._Value * multiplicator); + } + return result; + } + public static XXXXXXXXXXXXXX?[] Multiply(this double multiplicator, XXXXXXXXXXXXXX?[] units) + => units.Multiply(multiplicator); + + // === ReadOnlySpan === + public static Span Multiply(this ReadOnlySpan units, double multiplicator) + { + if (units.Length == 0) return []; + Span result = new XXXXXXXXXXXXXX[units.Length]; + Extensions.MultiplyCore(units, multiplicator, result); + return result; + } + public static Span Multiply(this double multiplicator, ReadOnlySpan units) + => units.Multiply(multiplicator); + public static Span Multiply(this ReadOnlySpan units, double multiplicator) + { + if (units.Length == 0) return []; + Span result = new XXXXXXXXXXXXXX?[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 XXXXXXXXXXXXXX(item.Value._Value * multiplicator); + } + return result; + } + public static Span Multiply(this double multiplicator, ReadOnlySpan units) + => units.Multiply(multiplicator); + + // === List === + public static List Multiply(this List units, double multiplicator) + { + if (units is null) return null!; + int len = units.Count; + if (len == 0) return []; + + var result = new List(len); + result.SetCountUnsafe(len); + Extensions.MultiplyCore(CollectionsMarshal.AsSpan(units), multiplicator, CollectionsMarshal.AsSpan(result)); + return result; + } + public static List Multiply(this double multiplicator, List units) + => units.Multiply(multiplicator); + public static List Multiply(this List units, double multiplicator) + { + if (units is null) return null!; + int len = units.Count; + if (len == 0) return []; + + var result = new List(units); + Span dstSpan = CollectionsMarshal.AsSpan(result); + for (int i = 0; i < len; i++) + { + ref var item = ref dstSpan[i]; + if (item.HasValue) item = new XXXXXXXXXXXXXX(item.Value._Value * multiplicator); + } + return result; + } + public static List Multiply(this double multiplicator, List units) + => units.Multiply(multiplicator); + + // === ICollection === + public static Tcoll Multiply(this ICollection units, double multiplicator) + where Tcoll : class, ICollection, new() + { + if (units is null) return null!; + var tColl = new Tcoll(); + if (tColl is List list) + list.Capacity = units.Count; + foreach (var item in units) + tColl.Add(new XXXXXXXXXXXXXX(item._Value * multiplicator)); + return tColl; + } + public static Tcoll Multiply(this double multiplicator, ICollection units) + where Tcoll : class, ICollection, new() => Multiply(units, multiplicator); + public static Tcoll Multiply(this ICollection units, double multiplicator) + where Tcoll : class, ICollection, new() + { + if (units is null) return null!; + var tColl = new Tcoll(); + if (tColl is List list) + list.Capacity = units.Count; + foreach (var item in units) + tColl.Add(new XXXXXXXXXXXXXX(item.HasValue ? item.Value._Value * multiplicator : 0d)); + return tColl; + } + public static Tcoll Multiply(this double multiplicator, ICollection units) + where Tcoll : class, ICollection, new() => Multiply(units, multiplicator); + + // === IEnumerable === + public static IEnumerable Multiply(this IEnumerable units, double multiplicator) + { + ArgumentNullException.ThrowIfNull(units); + foreach (var item in units) + yield return new XXXXXXXXXXXXXX(item._Value * multiplicator); + } + public static IEnumerable Multiply(this double multiplicator, IEnumerable units) + => units.Multiply(multiplicator); + public static IEnumerable Multiply(this IEnumerable units, double multiplicator) + { + ArgumentNullException.ThrowIfNull(units); + foreach (var item in units) + yield return item.HasValue + ? new XXXXXXXXXXXXXX(item.Value._Value * multiplicator) : null; + } + public static IEnumerable Multiply(this double multiplicator, IEnumerable units) + => units.Multiply(multiplicator); + + // ========================================== + // === DIVIDE === + // ========================================== + + public static XXXXXXXXXXXXXX[] Divide(this XXXXXXXXXXXXXX[] 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 XXXXXXXXXXXXXX[units.Length]; + Extensions.DivideCore(units, divisor, result); + return result; + } + public static XXXXXXXXXXXXXX[] Divide(this double dividend, XXXXXXXXXXXXXX[] units) + { + if (units is null) return null!; + if (units.Length == 0) return []; + + var result = new XXXXXXXXXXXXXX[units.Length]; + Extensions.DivideCore(dividend, units, result); // Вызов зеркального ядра (число / массив) + return result; + } + public static XXXXXXXXXXXXXX?[] Divide(this XXXXXXXXXXXXXX?[] 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 = (XXXXXXXXXXXXXX?[])units.Clone(); + int len = result.Length; + for (int i = 0; i < len; i++) + { + ref var item = ref result[i]; + if (item.HasValue) item = new XXXXXXXXXXXXXX(item.Value._Value / divisor); + } + return result; + } + public static XXXXXXXXXXXXXX?[] Divide(this double dividend, XXXXXXXXXXXXXX?[] units) + { + if (units is null) return null!; + if (units.Length == 0) return []; + + var result = (XXXXXXXXXXXXXX?[])units.Clone(); + int len = result.Length; + for (int i = 0; i < len; i++) + { + ref var item = ref result[i]; + if (item.HasValue) item = new XXXXXXXXXXXXXX(dividend / item.Value._Value); + } + return result; + } + + // === ReadOnlySpan === + public static Span Divide(this ReadOnlySpan units, double divisor) + { + if (units.Length == 0) return []; + if (divisor == 0 || double.IsNaN(divisor)) throw new ArgumentException("Делитель не может быть 0 или NaN.", nameof(divisor)); + var result = new XXXXXXXXXXXXXX[units.Length]; + Extensions.DivideCore(units, divisor, result); + return result; + } + public static Span Divide(this double dividend, ReadOnlySpan units) + { + if (units.Length == 0) return []; + var result = new XXXXXXXXXXXXXX[units.Length]; + Extensions.DivideCore(dividend, units, result); + return result; + } + public static Span Divide(this ReadOnlySpan units, double divisor) + { + if (units.Length == 0) return []; + if (divisor == 0 || double.IsNaN(divisor)) + throw new ArgumentException("Делитель не может быть 0 или NaN.", nameof(divisor)); + + Span result = new XXXXXXXXXXXXXX?[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 XXXXXXXXXXXXXX(item.Value._Value / divisor); + } + return result; + } + public static Span Divide(this double dividend, ReadOnlySpan units) + { + if (units.Length == 0) return []; + Span result = new XXXXXXXXXXXXXX?[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 XXXXXXXXXXXXXX(dividend / item.Value._Value); + } + return result; + } + + // === List === + public static List Divide(this List 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(len); + result.SetCountUnsafe(len); + Extensions.DivideCore(CollectionsMarshal.AsSpan(units), divisor, CollectionsMarshal.AsSpan(result)); + return result; + } + public static List Divide(this double dividend, List units) + { + if (units is null) return null!; + int len = units.Count; + if (len == 0) return []; + + var result = new List(len); + result.SetCountUnsafe(len); + Extensions.DivideCore(dividend, CollectionsMarshal.AsSpan(units), CollectionsMarshal.AsSpan(result)); + return result; + } + public static List Divide(this List 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(units); + Span dstSpan = CollectionsMarshal.AsSpan(result); + for (int i = 0; i < len; i++) + { + ref var item = ref dstSpan[i]; + if (item.HasValue) item = new XXXXXXXXXXXXXX(item.Value._Value / divisor); + } + return result; + } + public static List Divide(this double dividend, List units) + { + if (units is null) return null!; + int len = units.Count; + if (len == 0) return []; + + var result = new List(units); + Span dstSpan = CollectionsMarshal.AsSpan(result); + for (int i = 0; i < len; i++) + { + ref var item = ref dstSpan[i]; + if (item.HasValue) item = new XXXXXXXXXXXXXX(dividend / item.Value._Value); + } + return result; + } + + // === ICollection === + public static Tcoll Divide(this ICollection units, double divisor) + where Tcoll : class, ICollection, new() + { + if (units is null) return null!; + var tColl = new Tcoll(); + if (tColl is List list) + list.Capacity = units.Count; + foreach (var item in units) + tColl.Add(new XXXXXXXXXXXXXX(item._Value / divisor)); + return tColl; + } + public static Tcoll Divide(this double dividend, ICollection units) + where Tcoll : class, ICollection, new() + { + if (units is null) return null!; + var tColl = new Tcoll(); + if (tColl is List list) + list.Capacity = units.Count; + foreach (var item in units) + tColl.Add(new XXXXXXXXXXXXXX(dividend / item._Value)); + return tColl; + } + public static Tcoll Divide(this ICollection units, double divisor) + where Tcoll : class, ICollection, new() + { + if (units is null) return null!; + var tColl = new Tcoll(); + if (tColl is List list) + list.Capacity = units.Count; + foreach (var item in units) + tColl.Add(new XXXXXXXXXXXXXX(item.HasValue ? item.Value._Value / divisor : 0d)); + return tColl; + } + public static Tcoll Divide(this double dividend, ICollection units) + where Tcoll : class, ICollection, new() + { + if (units is null) return null!; + var tColl = new Tcoll(); + if (tColl is List list) + list.Capacity = units.Count; + foreach (var item in units) + tColl.Add(new XXXXXXXXXXXXXX(item.HasValue ? dividend / item.Value._Value : 0d)); + return tColl; + } + + // === IEnumerable === + public static IEnumerable Divide(this IEnumerable units, double divisor) + { + ArgumentNullException.ThrowIfNull(units); + foreach (var item in units) + yield return new XXXXXXXXXXXXXX(item._Value * divisor); + } + public static IEnumerable Divide(this double dividend, IEnumerable units) + { + ArgumentNullException.ThrowIfNull(units); + foreach (var item in units) + yield return new XXXXXXXXXXXXXX(dividend / item._Value); + } + public static IEnumerable Divide(this IEnumerable units, double divisor) + { + ArgumentNullException.ThrowIfNull(units); + foreach (var item in units) + yield return item.HasValue + ? new XXXXXXXXXXXXXX(item.Value._Value * divisor) : null; + } + public static IEnumerable Divide(this double dividend, IEnumerable units) + { + ArgumentNullException.ThrowIfNull(units); + foreach (var item in units) + yield return item.HasValue + ? new XXXXXXXXXXXXXX(dividend / item.Value._Value) : null; + } + + // ========================================== + // === PLUS === + // ========================================== + + public static XXXXXXXXXXXXXX[] Plus(this XXXXXXXXXXXXXX[] units, double summand) + { + if (units is null) return null!; + if (units.Length == 0) return []; + + var result = new XXXXXXXXXXXXXX[units.Length]; + Extensions.PlusCore(units, summand, result); + return result; + } + public static XXXXXXXXXXXXXX[] Plus(this double summand, XXXXXXXXXXXXXX[] units) + => units.Plus(summand); + public static XXXXXXXXXXXXXX?[] Plus(this XXXXXXXXXXXXXX?[] units, double summand) + { + if (units is null) return null!; + if (units.Length == 0) return []; + + var result = (XXXXXXXXXXXXXX?[])units.Clone(); + int len = result.Length; + for (int i = 0; i < len; i++) + { + ref var item = ref result[i]; + if (item.HasValue) item = new XXXXXXXXXXXXXX(item.Value._Value + summand); + } + return result; + } + public static XXXXXXXXXXXXXX?[] Plus(this double summand, XXXXXXXXXXXXXX?[] units) + => units.Plus(summand); + + // === ReadOnlySpan === + public static Span Plus(this ReadOnlySpan units, double summand) + { + if (units.Length == 0) return []; + var result = new XXXXXXXXXXXXXX[units.Length]; + Extensions.PlusCore(units, summand, result); + return result; + } + public static Span Plus(this double summand, ReadOnlySpan units) + => units.Plus(summand); + public static Span Plus(this ReadOnlySpan units, double summand) + { + if (units.Length == 0) return []; + Span result = new XXXXXXXXXXXXXX?[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 XXXXXXXXXXXXXX(item.Value._Value + summand); + } + return result; + } + public static Span Plus(this double summand, ReadOnlySpan units) + => units.Multiply(summand); + + // === List === + public static List Plus(this List units, double summand) + { + if (units is null) return null!; + int len = units.Count; + if (len == 0) return []; + + var result = new List(len); + result.SetCountUnsafe(len); + Extensions.PlusCore(CollectionsMarshal.AsSpan(units), summand, CollectionsMarshal.AsSpan(result)); + return result; + } + public static List Plus(this double summand, List units) + => units.Plus(summand); + public static List Plus(this List units, double summand) + { + if (units is null) return null!; + int len = units.Count; + if (len == 0) return []; + + var result = new List(units); + Span dstSpan = CollectionsMarshal.AsSpan(result); + for (int i = 0; i < len; i++) + { + ref var item = ref dstSpan[i]; + if (item.HasValue) item = new XXXXXXXXXXXXXX(item.Value._Value + summand); + } + return result; + } + public static List Plus(this double summand, List units) + => units.Plus(summand); + + // === ICollection === + public static Tcoll Plus(this ICollection units, double summand) + where Tcoll : class, ICollection, new() + { + if (units is null) return null!; + var tColl = new Tcoll(); + if (tColl is List list) + list.Capacity = units.Count; + foreach (var item in units) + tColl.Add(new XXXXXXXXXXXXXX(item._Value + summand)); + return tColl; + } + public static Tcoll Plus(this double summand, ICollection units) + where Tcoll : class, ICollection, new() => Plus(units, summand); + public static Tcoll Plus(this ICollection units, double summand) + where Tcoll : class, ICollection, new() + { + if (units is null) return null!; + var tColl = new Tcoll(); + if (tColl is List list) + list.Capacity = units.Count; + foreach (var item in units) + tColl.Add(new XXXXXXXXXXXXXX(item.HasValue ? item.Value._Value + summand : 0d)); + return tColl; + } + public static Tcoll Plus(this double summand, ICollection units) + where Tcoll : class, ICollection, new() => Plus(units, summand); + + // === IEnumerable === + public static IEnumerable Plus(this IEnumerable units, double summand) + { + ArgumentNullException.ThrowIfNull(units); + foreach (var item in units) + yield return new XXXXXXXXXXXXXX(item._Value + summand); + } + public static IEnumerable Plus(this double summand, IEnumerable units) + => units.Plus(summand); + public static IEnumerable Plus(this IEnumerable units, double summand) + { + ArgumentNullException.ThrowIfNull(units); + foreach (var item in units) + yield return item.HasValue + ? new XXXXXXXXXXXXXX(item.Value._Value + summand) : null; + } + public static IEnumerable Plus(this double summand, IEnumerable units) + => units.Plus(summand); + + // ========================================== + // === MINUS === + // ========================================== + + public static XXXXXXXXXXXXXX[] Minus(this XXXXXXXXXXXXXX[] units, double subtrahend) + { + if (units is null) return null!; + if (units.Length == 0) return []; + + var result = new XXXXXXXXXXXXXX[units.Length]; + Extensions.MinusCore(units, subtrahend, result); + return result; + } + public static XXXXXXXXXXXXXX[] Minus(this double minuend, XXXXXXXXXXXXXX[] units) + { + if (units is null) return null!; + if (units.Length == 0) return []; + + var result = new XXXXXXXXXXXXXX[units.Length]; + Extensions.MinusCore(minuend, units, result); // Вызов зеркального ядра (число - массив) + return result; + } + public static XXXXXXXXXXXXXX?[] Minus(this XXXXXXXXXXXXXX?[] units, double subtrahend) + { + if (units is null) return null!; + if (units.Length == 0) return []; + + var result = (XXXXXXXXXXXXXX?[])units.Clone(); + int len = result.Length; + for (int i = 0; i < len; i++) + { + ref var item = ref result[i]; + if (item.HasValue) item = new XXXXXXXXXXXXXX(item.Value._Value - subtrahend); + } + return result; + } + public static XXXXXXXXXXXXXX?[] Minus(this double minuend, XXXXXXXXXXXXXX?[] units) + { + if (units is null) return null!; + if (units.Length == 0) return []; + + var result = (XXXXXXXXXXXXXX?[])units.Clone(); + int len = result.Length; + for (int i = 0; i < len; i++) + { + ref var item = ref result[i]; + if (item.HasValue) item = new XXXXXXXXXXXXXX(minuend - item.Value._Value); + } + return result; + } + + // === ReadOnlySpan === + public static Span Minus(this ReadOnlySpan units, double subtrahend) + { + if (units.Length == 0) return []; + var result = new XXXXXXXXXXXXXX[units.Length]; + Extensions.MinusCore(units, subtrahend, result); + return result; + } + public static Span Minus(this double minuend, ReadOnlySpan units) + { + if (units.Length == 0) return []; + var result = new XXXXXXXXXXXXXX[units.Length]; + Extensions.MinusCore(minuend, units, result); + return result; + } + public static Span Minus(this ReadOnlySpan units, double subtrahend) + { + if (units.Length == 0) return []; + Span result = new XXXXXXXXXXXXXX?[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 XXXXXXXXXXXXXX(item.Value._Value - subtrahend); + } + return result; + } + public static Span Minus(this double minuend, ReadOnlySpan units) + { + if (units.Length == 0) return []; + Span result = new XXXXXXXXXXXXXX?[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 XXXXXXXXXXXXXX(minuend - item.Value._Value); + } + return result; + } + + // === List === + public static List Minus(this List units, double subtrahend) + { + if (units is null) return null!; + int len = units.Count; + if (len == 0) return []; + + var result = new List(len); + result.SetCountUnsafe(len); + Extensions.MinusCore(CollectionsMarshal.AsSpan(units), subtrahend, CollectionsMarshal.AsSpan(result)); + return result; + } + public static List Minus(this double minuend, List units) + { + if (units is null) return null!; + int len = units.Count; + if (len == 0) return []; + + var result = new List(len); + result.SetCountUnsafe(len); + Extensions.MinusCore(minuend, CollectionsMarshal.AsSpan(units), CollectionsMarshal.AsSpan(result)); + return result; + } + public static List Minus(this List units, double subtrahend) + { + if (units is null) return null!; + int len = units.Count; + if (len == 0) return []; + + var result = new List(units); + Span dstSpan = CollectionsMarshal.AsSpan(result); + for (int i = 0; i < len; i++) + { + ref var item = ref dstSpan[i]; + if (item.HasValue) item = new XXXXXXXXXXXXXX(item.Value._Value - subtrahend); + } + return result; + } + public static List Minus(this double minuend, List units) + { + if (units is null) return null!; + int len = units.Count; + if (len == 0) return []; + + var result = new List(units); + Span dstSpan = CollectionsMarshal.AsSpan(result); + for (int i = 0; i < len; i++) + { + ref var item = ref dstSpan[i]; + if (item.HasValue) item = new XXXXXXXXXXXXXX(minuend - item.Value._Value); + } + return result; + } + + // === ICollection === + public static Tcoll Minus(this ICollection units, double subtrahend) + where Tcoll : class, ICollection, new() + { + if (units is null) return null!; + var tColl = new Tcoll(); + if (tColl is List list) + list.Capacity = units.Count; + foreach (var item in units) + tColl.Add(new XXXXXXXXXXXXXX(item._Value - subtrahend)); + return tColl; + } + public static Tcoll Minus(this double minuend, ICollection units) + where Tcoll : class, ICollection, new() + { + if (units is null) return null!; + var tColl = new Tcoll(); + if (tColl is List list) + list.Capacity = units.Count; + foreach (var item in units) + tColl.Add(new XXXXXXXXXXXXXX(minuend - item._Value)); + return tColl; + } + public static Tcoll Minus(this ICollection units, double subtrahend) + where Tcoll : class, ICollection, new() + { + if (units is null) return null!; + var tColl = new Tcoll(); + if (tColl is List list) + list.Capacity = units.Count; + foreach (var item in units) + tColl.Add(new XXXXXXXXXXXXXX(item.HasValue ? item.Value._Value - subtrahend : 0d)); + return tColl; + } + public static Tcoll Minus(this double minuend, ICollection units) + where Tcoll : class, ICollection, new() + { + if (units is null) return null!; + var tColl = new Tcoll(); + if (tColl is List list) + list.Capacity = units.Count; + foreach (var item in units) + tColl.Add(new XXXXXXXXXXXXXX(item.HasValue ? minuend - item.Value._Value : 0d)); + return tColl; + } + + // === IEnumerable === + public static IEnumerable Minus(this IEnumerable units, double subtrahend) + { + ArgumentNullException.ThrowIfNull(units); + foreach (var item in units) + yield return new XXXXXXXXXXXXXX(item._Value - subtrahend); + } + public static IEnumerable Minus(this double minuend, IEnumerable units) + { + ArgumentNullException.ThrowIfNull(units); + foreach (var item in units) + yield return new XXXXXXXXXXXXXX(minuend - item._Value); + } + public static IEnumerable Minus(this IEnumerable units, double subtrahend) + { + ArgumentNullException.ThrowIfNull(units); + foreach (var item in units) + yield return item.HasValue + ? new XXXXXXXXXXXXXX(item.Value._Value - subtrahend) : null; + } + public static IEnumerable Minus(this double minuend, IEnumerable units) + { + ArgumentNullException.ThrowIfNull(units); + foreach (var item in units) + yield return item.HasValue + ? new XXXXXXXXXXXXXX(minuend - item.Value._Value) : null; + } + + // ========================================== + // ЦЕЛАЯ СТЕПЕНЬ (int power) + // ========================================== + + public static XXXXXXXXXXXXXX[] Pow(this XXXXXXXXXXXXXX[] units, int power) + { + if (units is null) return null!; + if (units.Length == 0) return []; + + var result = new XXXXXXXXXXXXXX[units.Length]; + Extensions.PowCore(units, power, result); + return result; + } + public static XXXXXXXXXXXXXX?[] Pow(this XXXXXXXXXXXXXX?[] units, int power) + { + if (units is null) return null!; + if (units.Length == 0) return []; + + var result = (XXXXXXXXXXXXXX?[])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 XXXXXXXXXXXXXX(item.Value._Value.QuickPow(power)); + } + } + return result; + } + + // === ReadOnlySpan === + public static Span Pow(this ReadOnlySpan units, int power) + { + if (units.Length == 0) return []; + var result = new XXXXXXXXXXXXXX[units.Length]; + Extensions.PowCore(units, power, result); + return result; + } + public static Span Pow(this ReadOnlySpan units, int power) + { + int len = units.Length; + if (len == 0) return []; + Span result = new XXXXXXXXXXXXXX?[units.Length]; + units.CopyTo(result); + for (int i = 0; i < len; i++) + { + ref var item = ref result[i]; + if (item.HasValue) item = new XXXXXXXXXXXXXX(item.Value._Value.QuickPow(power)); + } + return result; + } + + // === List === + public static List Pow(this List units, int power) + { + if (units is null) return null!; + int len = units.Count; + if (len == 0) return []; + + var result = new List(len); + result.SetCountUnsafe(len); + Extensions.PowCore(CollectionsMarshal.AsSpan(units), power, CollectionsMarshal.AsSpan(result)); + return result; + } + public static List Pow(this List units, int power) + { + if (units is null) return null!; + int len = units.Count; + if (len == 0) return []; + + var result = new List(units); + Span dstSpan = CollectionsMarshal.AsSpan(result); + for (int i = 0; i < len; i++) + { + ref var item = ref dstSpan[i]; + if (item.HasValue) item = new XXXXXXXXXXXXXX(item.Value._Value.QuickPow(power)); + } + return result; + } + + // === ICollection === + public static Tcoll Pow(this ICollection units, int power) + where Tcoll : class, ICollection, new() + { + if (units is null) return null!; + var tColl = new Tcoll(); + if (tColl is List list) + list.Capacity = units.Count; + foreach (var item in units) + tColl.Add(new XXXXXXXXXXXXXX(item._Value.QuickPow(power))); + return tColl; + } + public static Tcoll Pow(this ICollection units, int power) + where Tcoll : class, ICollection, new() + { + if (units is null) return null!; + var tColl = new Tcoll(); + if (tColl is List list) + list.Capacity = units.Count; + foreach (var item in units) + tColl.Add(new XXXXXXXXXXXXXX(item.HasValue ? item.Value._Value.QuickPow(power) : 0d)); + return tColl; + } + + // === IEnumerable === + public static IEnumerable Pow(this IEnumerable units, int power) + { + ArgumentNullException.ThrowIfNull(units); + foreach (var item in units) + yield return new XXXXXXXXXXXXXX(item._Value.QuickPow(power)); + } + public static IEnumerable Pow(this IEnumerable units, int power) + { + ArgumentNullException.ThrowIfNull(units); + foreach (var item in units) + yield return item.HasValue + ? new XXXXXXXXXXXXXX(item.Value._Value.QuickPow(power)) : null; + } + + // ========================================== + // ДРОБНАЯ СТЕПЕНЬ (double power) + // ========================================== + + public static XXXXXXXXXXXXXX[] Pow(this XXXXXXXXXXXXXX[] units, double power) + { + if (units is null) return null!; + if (units.Length == 0) return []; + + var result = new XXXXXXXXXXXXXX[units.Length]; + Extensions.PowCore(units, power, result); + return result; + } + public static XXXXXXXXXXXXXX?[] Pow(this XXXXXXXXXXXXXX?[] units, double power) + { + if (units is null) return null!; + if (units.Length == 0) return []; + + var result = (XXXXXXXXXXXXXX?[])units.Clone(); + int len = result.Length; + for (int i = 0; i < len; i++) + { + ref var item = ref result[i]; + if (item.HasValue) + { + item = new XXXXXXXXXXXXXX(Math.Pow(item.Value._Value, power)); + } + } + return result; + } + + // === ReadOnlySpan === + public static Span Pow(this ReadOnlySpan units, double power) + { + if (units.Length == 0) return []; + var result = new XXXXXXXXXXXXXX[units.Length]; + Extensions.PowCore(units, power, result); + return result; + } + public static Span Pow(this ReadOnlySpan units, double power) + { + int len = units.Length; + if (len == 0) return []; + Span result = new XXXXXXXXXXXXXX?[units.Length]; + units.CopyTo(result); + for (int i = 0; i < len; i++) + { + ref var item = ref result[i]; + if (item.HasValue) item = new XXXXXXXXXXXXXX(Math.Pow(item.Value._Value, power)); + } + return result; + } + + // === List === + public static List Pow(this List units, double power) + { + if (units is null) return null!; + int len = units.Count; + if (len == 0) return []; + + var result = new List(len); + result.SetCountUnsafe(len); + Extensions.PowCore(CollectionsMarshal.AsSpan(units), power, CollectionsMarshal.AsSpan(result)); + return result; + } + public static List Pow(this List units, double power) + { + if (units is null) return null!; + int len = units.Count; + if (len == 0) return []; + + var result = new List(units); + Span dstSpan = CollectionsMarshal.AsSpan(result); + for (int i = 0; i < len; i++) + { + ref var item = ref dstSpan[i]; + if (item.HasValue) item = new XXXXXXXXXXXXXX(Math.Pow(item.Value._Value, power)); + } + return result; + } + + // === ICollection === + public static Tcoll Pow(this ICollection units, double power) + where Tcoll : class, ICollection, new() + { + if (units is null) return null!; + var tColl = new Tcoll(); + if (tColl is List list) + list.Capacity = units.Count; + foreach (var item in units) + tColl.Add(new XXXXXXXXXXXXXX(Math.Pow(item._Value, power))); + return tColl; + } + public static Tcoll Pow(this ICollection units, double power) + where Tcoll : class, ICollection, new() + { + if (units is null) return null!; + var tColl = new Tcoll(); + if (tColl is List list) + list.Capacity = units.Count; + foreach (var item in units) + tColl.Add(new XXXXXXXXXXXXXX(item.HasValue ? Math.Pow(item.Value._Value, power) : 0d)); + return tColl; + } + + // === IEnumerable === + public static IEnumerable Pow(this IEnumerable units, double power) + { + ArgumentNullException.ThrowIfNull(units); + foreach (var item in units) + yield return new XXXXXXXXXXXXXX(Math.Pow(item._Value, power)); + } + public static IEnumerable Pow(this IEnumerable units, double power) + { + ArgumentNullException.ThrowIfNull(units); + foreach (var item in units) + yield return item.HasValue + ? new XXXXXXXXXXXXXX(Math.Pow(item.Value._Value, power)) : null; + } + + + // ========================================== + // КВАДРАТНЫЙ КОРЕНЬ (Sqrt) + // ========================================== + + public static XXXXXXXXXXXXXX[] Sqrt(this XXXXXXXXXXXXXX[] units) + { + if (units is null) return null!; + if (units.Length == 0) return []; + + var result = new XXXXXXXXXXXXXX[units.Length]; + Extensions.SqrtCore(units, result); + return result; + } + public static XXXXXXXXXXXXXX?[] Sqrt(this XXXXXXXXXXXXXX?[] units) + { + if (units is null) return null!; + if (units.Length == 0) return []; + + var result = (XXXXXXXXXXXXXX?[])units.Clone(); + int len = result.Length; + for (int i = 0; i < len; i++) + { + ref var item = ref result[i]; + if (item.HasValue) + { + item = new XXXXXXXXXXXXXX(Math.Sqrt(item.Value._Value)); + } + } + return result; + } + + // === ReadOnlySpan === + public static Span Sqrt(this ReadOnlySpan units) + { + if (units.Length == 0) return []; + var result = new XXXXXXXXXXXXXX[units.Length]; + Extensions.SqrtCore(units, result); + return result; + } + public static Span Sqrt(this ReadOnlySpan units) + { + int len = units.Length; + if (len == 0) return []; + Span result = new XXXXXXXXXXXXXX?[units.Length]; + units.CopyTo(result); + for (int i = 0; i < len; i++) + { + ref var item = ref result[i]; + if (item.HasValue) item = new XXXXXXXXXXXXXX(Math.Sqrt(item.Value._Value)); + } + return result; + } + + // === List === + public static List Sqrt(this List units) + { + if (units is null) return null!; + int len = units.Count; + if (len == 0) return []; + + var result = new List(len); + result.SetCountUnsafe(len); + Extensions.SqrtCore(CollectionsMarshal.AsSpan(units), CollectionsMarshal.AsSpan(result)); + return result; + } + public static List Sqrt(this List units) + { + if (units is null) return null!; + int len = units.Count; + if (len == 0) return []; + + var result = new List(units); + Span dstSpan = CollectionsMarshal.AsSpan(result); + for (int i = 0; i < len; i++) + { + ref var item = ref dstSpan[i]; + if (item.HasValue) item = new XXXXXXXXXXXXXX(Math.Sqrt(item.Value._Value)); + } + return result; + } + + // === ICollection === + public static Tcoll Pow(this ICollection units) + where Tcoll : class, ICollection, new() + { + if (units is null) return null!; + var tColl = new Tcoll(); + if (tColl is List list) + list.Capacity = units.Count; + foreach (var item in units) + tColl.Add(new XXXXXXXXXXXXXX(Math.Sqrt(item._Value))); + return tColl; + } + public static Tcoll Pow(this ICollection units) + where Tcoll : class, ICollection, new() + { + if (units is null) return null!; + var tColl = new Tcoll(); + if (tColl is List list) + list.Capacity = units.Count; + foreach (var item in units) + tColl.Add(new XXXXXXXXXXXXXX(item.HasValue ? Math.Sqrt(item.Value._Value) : 0d)); + return tColl; + } + + // === IEnumerable === + public static IEnumerable Pow(this IEnumerable units) + { + ArgumentNullException.ThrowIfNull(units); + foreach (var item in units) + yield return new XXXXXXXXXXXXXX(Math.Sqrt(item._Value)); + } + public static IEnumerable Pow(this IEnumerable units) + { + ArgumentNullException.ThrowIfNull(units); + foreach (var item in units) + yield return item.HasValue + ? new XXXXXXXXXXXXXX(Math.Sqrt(item.Value._Value)) : null; + } +} + +public class XXXXXXXXXXXXXXConverter : JsonConverter +{ + // Используем инвариантную культуру, чтобы разделителем всегда была точка (10.5, а не 10,5) + private static readonly CultureInfo Culture = CultureInfo.InvariantCulture; + + public override XXXXXXXXXXXXXX Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + double double_Value; + + if (reader.TokenType == JsonTokenType.String) + { + // Безопасно парсим double из строки с поддержкой точки как разделителя + if (!double.TryParse(reader.GetString(), NumberStyles.Float, Culture, out double_Value)) + { + throw new JsonException($"Не удалось преобразовать строковое значение в double для метрики {nameof(XXXXXXXXXXXXXX)}."); + } + } + else + { + // Прямое быстрое чтение числа из JSON + double_Value = reader.GetDouble(); + } + + return new(double_Value); + } + + public override void Write(Utf8JsonWriter writer, XXXXXXXXXXXXXX value, JsonSerializerOptions options) + { + // Записываем число напрямую в байтовый буфер без выделения памяти под строки + writer.WriteNumberValue(value._Value); + } + + public override void WriteAsPropertyName(Utf8JsonWriter writer, XXXXXXXXXXXXXX value, JsonSerializerOptions options) + { + // Ключи JSON-объектов всегда должны быть строками. + // Форматируем double в строку с точкой, чтобы другие сервисы экосистемы прочитали её корректно. + // Формат "R" (Round-trip) гарантирует, что число не потеряет точность при обратном парсинге. + writer.WritePropertyName(value._Value.ToString("R", Culture)); + } + + public override XXXXXXXXXXXXXX ReadAsPropertyName(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + string propertyName = reader.GetString()!; + + if (!double.TryParse(propertyName, NumberStyles.Float, Culture, out double double_Value)) + { + throw new JsonException($"Невалидное числовое значение в ключе свойства JSON: '{propertyName}' для метрики {nameof(XXXXXXXXXXXXXX)}."); + } + + return new(double_Value); + } +} +#endif \ No newline at end of file diff --git a/QWERTYkez.Mensura/Units/XXXXXXXX.Ref.cs b/QWERTYkez.Mensura/Units/XXXXXXXX.Ref.cs index 1962d5d..2bd2066 100644 --- a/QWERTYkez.Mensura/Units/XXXXXXXX.Ref.cs +++ b/QWERTYkez.Mensura/Units/XXXXXXXX.Ref.cs @@ -1,74 +1,74 @@ -//namespace QWERTYkez.Mensura.Units; +#if DEBUG +namespace QWERTYkez.Mensura.Units; -///// -///// Base value is MilliMeters -///// -//[UnitOperatorsGenerator, DebuggerDisplay("mm = {_MilliMeters.ToString(\"0.###\")}, m = {Meters.ToString(\"0.###\")}")] -//public readonly partial record struct XXXXXXXXXXXXXX -//{ -// public static XXXXXXXXXXXXXX MilliMeter { get; } = new(1); -//#pragma warning disable IDE1006 // _Подчеркивание для обозначения того, что это базовая единица -// [NotMapped, JsonIgnore] public double _MilliMeters { get => _Value; init => _Value = value; } -//#pragma warning restore IDE1006 +/// +/// Base value is MilliMeters +/// +[DebuggerDisplay("mm = {_MilliMeters.ToString(\"0.###\")}, m = {Meters.ToString(\"0.###\")}")] +public readonly partial record struct XXXXXXXXXXXXXX +{ + public static XXXXXXXXXXXXXX MilliMeter { get; } = new(1); + [NotMapped, JsonIgnore] public double _MilliMeters { get => _Value; init => _Value = value; } -// public static XXXXXXXXXXXXXX CentiMeter { get; } = new(XXXXXXXXXXXXXXConv.CentiMeters.To(1)); -// [NotMapped, JsonIgnore] public double CentiMeters -// { -// get => XXXXXXXXXXXXXXConv.CentiMeters.From(_Value); -// init -// { -// XXXXXXXXXXXXXX aaa = new(); -// XXXXXXXXXXXXXX bbb = new(); + public static XXXXXXXXXXXXXX CentiMeter { get; } = new(XXXXXXXXXXXXXXConv.CentiMeters.To(1)); + [NotMapped, JsonIgnore] public double CentiMeters + { + get => XXXXXXXXXXXXXXConv.CentiMeters.From(_Value); + init + { + XXXXXXXXXXXXXX aaa = new(); + XXXXXXXXXXXXXX bbb = new(); -// if (aaa != bbb || aaa == bbb) -// { + if (aaa != bbb || aaa == bbb) + { -// } + } -// _Value = XXXXXXXXXXXXXXConv.CentiMeters.To(value); -// } -// } + _Value = XXXXXXXXXXXXXXConv.CentiMeters.To(value); + } + } -// public static XXXXXXXXXXXXXX DeciMeter { get; } = new(XXXXXXXXXXXXXXConv.DeciMeters.To(1)); -// [NotMapped, JsonIgnore] public double DeciMeters -// { -// get => XXXXXXXXXXXXXXConv.DeciMeters.From(_Value); -// init => _Value = XXXXXXXXXXXXXXConv.DeciMeters.To(value); -// } + public static XXXXXXXXXXXXXX DeciMeter { get; } = new(XXXXXXXXXXXXXXConv.DeciMeters.To(1)); + [NotMapped, JsonIgnore] public double DeciMeters + { + get => XXXXXXXXXXXXXXConv.DeciMeters.From(_Value); + init => _Value = XXXXXXXXXXXXXXConv.DeciMeters.To(value); + } -// public static XXXXXXXXXXXXXX Meter { get; } = new(XXXXXXXXXXXXXXConv.Meters.To(1)); -// [NotMapped, JsonIgnore] public double Meters -// { -// get => XXXXXXXXXXXXXXConv.Meters.From(_Value); -// init => _Value = XXXXXXXXXXXXXXConv.Meters.To(value); -// } + public static XXXXXXXXXXXXXX Meter { get; } = new(XXXXXXXXXXXXXXConv.Meters.To(1)); + [NotMapped, JsonIgnore] public double Meters + { + get => XXXXXXXXXXXXXXConv.Meters.From(_Value); + init => _Value = XXXXXXXXXXXXXXConv.Meters.To(value); + } -// public static XXXXXXXXXXXXXX KiloMeter { get; } = new(XXXXXXXXXXXXXXConv.KiloMeters.To(1)); -// [NotMapped, JsonIgnore] public double KiloMeters -// { -// get => XXXXXXXXXXXXXXConv.KiloMeters.From(_Value); -// init => _Value = XXXXXXXXXXXXXXConv.KiloMeters.To(value); -// } + public static XXXXXXXXXXXXXX KiloMeter { get; } = new(XXXXXXXXXXXXXXConv.KiloMeters.To(1)); + [NotMapped, JsonIgnore] public double KiloMeters + { + get => XXXXXXXXXXXXXXConv.KiloMeters.From(_Value); + init => _Value = XXXXXXXXXXXXXXConv.KiloMeters.To(value); + } -// public XXXXXXXXXXXXXX AddMilliMeters(double value) => new(_Value + value); -// public XXXXXXXXXXXXXX AddCentiMeters(double value) => new(_Value + XXXXXXXXXXXXXXConv.CentiMeters.To(value)); -// public XXXXXXXXXXXXXX AddDeciMeters(double value) => new(_Value + XXXXXXXXXXXXXXConv.DeciMeters.To(value)); -// public XXXXXXXXXXXXXX AddMeters(double value) => new(_Value + XXXXXXXXXXXXXXConv.Meters.To(value)); -// public XXXXXXXXXXXXXX AddKiloMeters(double value) => new(_Value + XXXXXXXXXXXXXXConv.KiloMeters.To(value)); -//} + public XXXXXXXXXXXXXX AddMilliMeters(double value) => new(_Value + value); + public XXXXXXXXXXXXXX AddCentiMeters(double value) => new(_Value + XXXXXXXXXXXXXXConv.CentiMeters.To(value)); + public XXXXXXXXXXXXXX AddDeciMeters(double value) => new(_Value + XXXXXXXXXXXXXXConv.DeciMeters.To(value)); + public XXXXXXXXXXXXXX AddMeters(double value) => new(_Value + XXXXXXXXXXXXXXConv.Meters.To(value)); + public XXXXXXXXXXXXXX AddKiloMeters(double value) => new(_Value + XXXXXXXXXXXXXXConv.KiloMeters.To(value)); +} -//internal readonly struct XXXXXXXXXXXXXXConv -//{ -// private XXXXXXXXXXXXXXConv(double multiplicator) => this.Multiplicator = multiplicator; -// public double To(double value) => value * Multiplicator; -// public double From(double value) => value / Multiplicator; -// public double Multiplicator { get; init; } -// public static XXXXXXXXXXXXXXConv MilliMeters { get; } = new(1); -// public static XXXXXXXXXXXXXXConv CentiMeters { get; } = new(10); -// public static XXXXXXXXXXXXXXConv DeciMeters { get; } = new(100); -// public static XXXXXXXXXXXXXXConv Meters { get; } = new(1000); -// public static XXXXXXXXXXXXXXConv KiloMeters { get; } = new(1000000); -//} \ No newline at end of file +internal readonly struct XXXXXXXXXXXXXXConv +{ + private XXXXXXXXXXXXXXConv(double multiplicator) => this.Multiplicator = multiplicator; + public double To(double value) => value * Multiplicator; + public double From(double value) => value / Multiplicator; + public double Multiplicator { get; init; } + public static XXXXXXXXXXXXXXConv MilliMeters { get; } = new(1); + public static XXXXXXXXXXXXXXConv CentiMeters { get; } = new(10); + public static XXXXXXXXXXXXXXConv DeciMeters { get; } = new(100); + public static XXXXXXXXXXXXXXConv Meters { get; } = new(1000); + public static XXXXXXXXXXXXXXConv KiloMeters { get; } = new(1000000); +} +#endif \ No newline at end of file