diff --git a/QWERTYkez.Mensura.Generator/OperatorsGenerator.cs b/QWERTYkez.Mensura.Generator/OperatorsGenerator.cs index 55c1725..a2e4838 100644 --- a/QWERTYkez.Mensura.Generator/OperatorsGenerator.cs +++ b/QWERTYkez.Mensura.Generator/OperatorsGenerator.cs @@ -14,7 +14,7 @@ internal class CollectionsOperatorsGenerator : IIncrementalGenerator private const string AttributeShortName = "CollectionsOperatorsGenerator"; private const string AttributeFullName = AttributeShortName + "Attribute"; - private const string AttributeSource = @"namespace MetricSystem.Generator; + private const string AttributeSource = @"namespace QWERTYkez.Mensura; [System.AttributeUsage(System.AttributeTargets.Class | System.AttributeTargets.Method, AllowMultiple = false, Inherited = false)] internal sealed class CollectionsOperatorsGeneratorAttribute : System.Attribute { }"; diff --git a/QWERTYkez.Mensura.Generator/QWERTYkez.Mensura.Generator.csproj b/QWERTYkez.Mensura.Generator/QWERTYkez.Mensura.Generator.csproj index 1b8fd95..c59ec9c 100644 --- a/QWERTYkez.Mensura.Generator/QWERTYkez.Mensura.Generator.csproj +++ b/QWERTYkez.Mensura.Generator/QWERTYkez.Mensura.Generator.csproj @@ -15,12 +15,12 @@ - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/QWERTYkez.Mensura.Generator/UnitOperatorsGenerator.cs b/QWERTYkez.Mensura.Generator/UnitOperatorsGenerator.cs index 62d2a53..bddb0ae 100644 --- a/QWERTYkez.Mensura.Generator/UnitOperatorsGenerator.cs +++ b/QWERTYkez.Mensura.Generator/UnitOperatorsGenerator.cs @@ -18,7 +18,7 @@ public class UnitOperatorsGenerator : IIncrementalGenerator context.RegisterPostInitializationOutput(ctx => { ctx.AddSource("UnitOperatorsGeneratorAttribute.g.cs", - SourceText.From(@"namespace MetricSystem.Generator + SourceText.From(@"namespace QWERTYkez.Mensura { [System.AttributeUsage(System.AttributeTargets.Struct | System.AttributeTargets.RecordStruct, AllowMultiple = false)] public sealed class UnitOperatorsGeneratorAttribute : System.Attribute { } diff --git a/QWERTYkez.Mensura/Extensions.cs b/QWERTYkez.Mensura/Extensions.cs new file mode 100644 index 0000000..03cce00 --- /dev/null +++ b/QWERTYkez.Mensura/Extensions.cs @@ -0,0 +1,315 @@ +namespace QWERTYkez.Mensura; + +public static partial class Extensions +{ + internal static double ToDouble(this sbyte number) => Convert.ToDouble(number); + internal static double ToDouble(this short number) => Convert.ToDouble(number); + internal static double ToDouble(this int number) => Convert.ToDouble(number); + internal static double ToDouble(this long number) => Convert.ToDouble(number); + internal static double ToDouble(this byte number) => Convert.ToDouble(number); + internal static double ToDouble(this ushort number) => Convert.ToDouble(number); + internal static double ToDouble(this uint number) => Convert.ToDouble(number); + internal static double ToDouble(this ulong number) => Convert.ToDouble(number); + internal static double ToDouble(this nint number) => Convert.ToDouble(number); + internal static double ToDouble(this nuint number) => Convert.ToDouble(number); + internal static double ToDouble(this float number) => Convert.ToDouble(number); + internal static double ToDouble(this decimal number) => Convert.ToDouble(number); + + internal static double ToDouble(this sbyte? number) => number is null ? 0d : Convert.ToDouble(number); + internal static double ToDouble(this short? number) => number is null ? 0d : Convert.ToDouble(number); + internal static double ToDouble(this int? number) => number is null ? 0d : Convert.ToDouble(number); + internal static double ToDouble(this long? number) => number is null ? 0d : Convert.ToDouble(number); + internal static double ToDouble(this byte? number) => number is null ? 0d : Convert.ToDouble(number); + internal static double ToDouble(this ushort? number) => number is null ? 0d : Convert.ToDouble(number); + internal static double ToDouble(this uint? number) => number is null ? 0d : Convert.ToDouble(number); + internal static double ToDouble(this ulong? number) => number is null ? 0d : Convert.ToDouble(number); + internal static double ToDouble(this nint? number) => number is null ? 0d : Convert.ToDouble(number); + internal static double ToDouble(this nuint? number) => number is null ? 0d : Convert.ToDouble(number); + internal static double ToDouble(this float? number) => number is null ? 0d : Convert.ToDouble(number); + internal static double ToDouble(this decimal? number) => number is null ? 0d : Convert.ToDouble(number); + +#if NET7_0_OR_GREATER + internal static double ToDouble(this Int128 number) => Convert.ToDouble(number); + internal static double ToDouble(this UInt128 number) => Convert.ToDouble(number); + + internal static double ToDouble(this Int128? number) => number is null ? 0d : Convert.ToDouble(number); + internal static double ToDouble(this UInt128? number) => number is null ? 0d : Convert.ToDouble(number); +#endif + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static unsafe void SetCountUnsafe(this List list, int count) + { + // Берем адрес управляемого объекта List в памяти + // Объект передается по ref-ссылке, преобразуется в указатель + ref var mimic = ref Unsafe.As, ListLayoutMimic>(ref list); + + // Меняем приватный размер напрямую в памяти объекта! + mimic.Size = count; + } + + + + + + + + + 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 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 ProtectValue(this IMetric? metric) => metric is null ? 0d : metric.Value; + + public static U Min(this U? T1, U? T2) where U : class, IMetric, new() => (T1.ProtectValue() < T2.ProtectValue() ? T1 : T2).Protect(); + public static U Min(this U T1, IEnumerable units) where U : class, IMetric, new() => (T1 ?? new()).Min((units ?? []).MaxBy(u => u.ProtectValue())); + + public static U Max(this U? T1, U? T2) where U : class, IMetric, new() => (T1.ProtectValue() > T2.ProtectValue() ? T1 : T2).Protect(); + public static U Max(this U T1, IEnumerable units) where U : class, IMetric, new() => (T1 ?? new()).Max((units ?? []).MaxBy(u => u.ProtectValue())); + + + //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 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.ProtectValue() }; + public static U Abs(this U? metric) where U : class, IMetric, new() => new() { Value = Math.Abs(metric.ProtectValue()) }; + + /// 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.ProtectValue(); + var b = B.ProtectValue(); + 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.ProtectValue(); + var c = C.ProtectValue(); + 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.ProtectValue(); + var c = C.ProtectValue(); + 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.ProtectValue(), val ?? 2) }; + public static Length Sqrt(this Area? metric) => new() { Value = Math.Sqrt(metric.ProtectValue()) }; + + + public static U MetricSum(this IEnumerable args) where U : IMetric, new() => new() { Value = args?.Where(t => t is not null).Sum(m => m.ProtectValue()) ?? 0d }; + public static U MetricAverage(this IEnumerable args) where U : IMetric, new() => new() { Value = args?.Average(m => m.ProtectValue()) ?? double.NaN }; + public static U MetricMax(this IEnumerable args) where U : IMetric, new() => new() { Value = args.Max(m => m.ProtectValue()) }; + public static U MetricMin(this IEnumerable args) where U : IMetric, new() => new() { Value = args.Min(m => m.ProtectValue()) }; + + + + 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 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.ProtectValue()) }; + } + 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.ProtectValue()) }; + } + + + 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).MetricAverage(); + } + + + + 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/ListLayoutMimic.cs b/QWERTYkez.Mensura/ListLayoutMimic.cs new file mode 100644 index 0000000..5154799 --- /dev/null +++ b/QWERTYkez.Mensura/ListLayoutMimic.cs @@ -0,0 +1,9 @@ +namespace QWERTYkez.Mensura; + +[StructLayout(LayoutKind.Sequential)] +internal unsafe struct ListLayoutMimic +{ + public T[] Items; + public int Size; + public int Version; +} \ No newline at end of file diff --git a/QWERTYkez.Mensura/QWERTYkez.Mensura.csproj b/QWERTYkez.Mensura/QWERTYkez.Mensura.csproj index ff82a64..f11f431 100644 --- a/QWERTYkez.Mensura/QWERTYkez.Mensura.csproj +++ b/QWERTYkez.Mensura/QWERTYkez.Mensura.csproj @@ -1,9 +1,26 @@ - + - net6.0 + net6.0;net7.0;net8.0;net9.0;net10.0 enable enable + latest + false + false + true + + + + + + + 10.0.8 + + + 10.0.8 + + + \ No newline at end of file diff --git a/QWERTYkez.Mensura/Units/Length.cs b/QWERTYkez.Mensura/Units/Length.cs new file mode 100644 index 0000000..d662fe1 --- /dev/null +++ b/QWERTYkez.Mensura/Units/Length.cs @@ -0,0 +1,1144 @@ +namespace QWERTYkez.Mensura.Units; + +/// +/// Base value is MilliMeters +/// + +[UnitOperatorsGenerator, DebuggerDisplay("mm = {_MilliMeters.ToString(\"0.###\")}, m = {Meters.ToString(\"0.###\")}")] +public readonly partial record struct Length +{ + public static Length MilliMeter { get; } = new(1); +#pragma warning disable IDE1006 // _Подчеркивание для обозначения того, что это базовая единица + [NotMapped, JsonIgnore] public double _MilliMeters { get => _Value; init => _Value = value; } +#pragma warning restore IDE1006 + + + public static Length CentiMeter { get; } = new(LengthConv.CentiMeters.To(1)); + [NotMapped, JsonIgnore] public double CentiMeters + { + get => LengthConv.CentiMeters.From(_Value); + init + { + Length aaa = new(); + Length bbb = new(); + + if (aaa != bbb || aaa == bbb) + { + + } + + _Value = LengthConv.CentiMeters.To(value); + } + } + + public static Length DeciMeter { get; } = new(LengthConv.DeciMeters.To(1)); + [NotMapped, JsonIgnore] public double DeciMeters + { + get => LengthConv.DeciMeters.From(_Value); + init => _Value = LengthConv.DeciMeters.To(value); + } + + public static Length Meter { get; } = new(LengthConv.Meters.To(1)); + [NotMapped, JsonIgnore] public double Meters + { + get => LengthConv.Meters.From(_Value); + init => _Value = LengthConv.Meters.To(value); + } + + public static Length KiloMeter { get; } = new(LengthConv.KiloMeters.To(1)); + [NotMapped, JsonIgnore] public double KiloMeters + { + get => LengthConv.KiloMeters.From(_Value); + init => _Value = LengthConv.KiloMeters.To(value); + } + + + public Length AddMilliMeters(double value) => new(_Value + value); + public Length AddCentiMeters(double value) => new(_Value + LengthConv.CentiMeters.To(value)); + public Length AddDeciMeters(double value) => new(_Value + LengthConv.DeciMeters.To(value)); + public Length AddMeters(double value) => new(_Value + LengthConv.Meters.To(value)); + public Length AddKiloMeters(double value) => new(_Value + LengthConv.KiloMeters.To(value)); + + + + + + + public static explicit operator Length(double val) => new(val); + public static explicit operator double(Length unit) => unit._Value; + + public Length Abs() => new(Math.Abs(_Value)); + + /// C^2 = this^2 + B^2 C = (this^2 + B^2).Sqrt(2) + public Length HypFromLeg(Length B) => new(Math.Sqrt(_Value * _Value + B._Value * B._Value)); + + /// C^2 = this^2 + B^2 C = (this^2 + B^2).Sqrt(2) + public Length HypFromLeg(Length? 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 Length LegFromHyp(Length C) => new(Math.Sqrt(C._Value * C._Value - _Value * _Value)); + + /// C^2 = this^2 + B^2 B = (C^2 - this^2).Sqrt(2) + public Length LegFromHyp(Length? 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 Length LegFromLeg(Length A) => new(Math.Sqrt(_Value * _Value - A._Value * A._Value)); + + /// this^2 = A^2 + B^2 B = (this^2 - A^2).Sqrt(2) + public Length LegFromLeg(Length? A) + { + double a = A is null ? 0d : A.Value._Value; + return new(Math.Sqrt(_Value * _Value - a * a)); + } +} + +public static class LengthExtension +{ + internal static double ToDouble(this Length? unit) => unit is null ? 0d : unit.Value._Value; + + public static Length MetricSum(this IEnumerable units) => new() { Value = units?.Sum(m => m._Value) ?? 0d }; + public static Length MetricAverage(this IEnumerable units) => new() { Value = units?.Average(m => m._Value) ?? double.NaN }; + public static Length MetricMax(this IEnumerable units) => new() { Value = units?.Max(m => m._Value) ?? double.MinValue }; + public static Length MetricMin(this IEnumerable units) => new() { Value = units?.Min(m => m._Value) ?? double.MaxValue }; + + public static Length MetricSum(this IEnumerable units) => new() { Value = units?.Sum(m => m.ToDouble()) ?? 0d }; + public static Length MetricAverage(this IEnumerable units) => new() { Value = units?.Average(m => m.ToDouble()) ?? double.NaN }; + public static Length MetricMax(this IEnumerable units) => new() { Value = units?.Max(m => m.ToDouble()) ?? double.MinValue }; + public static Length 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 vectorizedValue = new Vector(value); + int vectorSize = Vector.Count; + int i = 0; + + ref double srcRef = ref MemoryMarshal.GetReference(srcDouble); + ref double dstRef = ref MemoryMarshal.GetReference(dstDouble); + + int SIMDEnd = len - (len % vectorSize); + for (; i < SIMDEnd; i += vectorSize) + { + ref double currentSrc = ref Unsafe.Add(ref srcRef, i); + ReadOnlySpan srcWindow = MemoryMarshal.CreateReadOnlySpan(ref currentSrc, vectorSize); + var vector = new Vector(srcWindow); + var multiplied = vector * vectorizedValue; + ref double currentDst = ref Unsafe.Add(ref dstRef, i); + Span dstWindow = MemoryMarshal.CreateSpan(ref currentDst, vectorSize); + multiplied.CopyTo(dstWindow); + } + + for (; i < len; i++) + { + Unsafe.Add(ref dstRef, i) = Unsafe.Add(ref srcRef, i) * value; + } + } + internal static void DivideCore(ReadOnlySpan 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 vectorizedValue = new Vector(divisor); + int vectorSize = Vector.Count; + int i = 0; + + ref double srcRef = ref MemoryMarshal.GetReference(srcDouble); + ref double dstRef = ref MemoryMarshal.GetReference(dstDouble); + + int SIMDEnd = len - (len % vectorSize); + for (; i < SIMDEnd; i += vectorSize) + { + ref double currentSrc = ref Unsafe.Add(ref srcRef, i); + ReadOnlySpan srcWindow = MemoryMarshal.CreateReadOnlySpan(ref currentSrc, vectorSize); + var vector = new Vector(srcWindow); + var multiplied = vector / vectorizedValue; + ref double currentDst = ref Unsafe.Add(ref dstRef, i); + Span dstWindow = MemoryMarshal.CreateSpan(ref currentDst, vectorSize); + multiplied.CopyTo(dstWindow); + } + + for (; i < len; i++) + { + Unsafe.Add(ref dstRef, i) = Unsafe.Add(ref srcRef, i) / divisor; + } + } + internal static void DivideCore(double dividend, ReadOnlySpan source, Span destination) + { + int len = source.Length; + ReadOnlySpan srcDouble = MemoryMarshal.Cast(source); + Span dstDouble = MemoryMarshal.Cast(destination); + + var vectorizedValue = new Vector(dividend); + var zeroVector = Vector.Zero; // Вектор из нулей для сравнения + int vectorSize = Vector.Count; + int i = 0; + + ref double srcRef = ref MemoryMarshal.GetReference(srcDouble); + ref double dstRef = ref MemoryMarshal.GetReference(dstDouble); + + int SIMDEnd = len - (len % vectorSize); + for (; i < SIMDEnd; i += vectorSize) + { + ref double currentSrc = ref Unsafe.Add(ref srcRef, i); + ReadOnlySpan srcWindow = MemoryMarshal.CreateReadOnlySpan(ref currentSrc, vectorSize); + var vector = new Vector(srcWindow); + + // БЫСТРАЯ ПРОВЕРКА: Есть ли хотя бы один 0.0 в текущем векторе? + if (Vector.EqualsAny(vector, zeroVector)) + { + throw new DivideByZeroException($"Обнаружен делитель, равный нулю, в районе индексов {i}..{i + vectorSize - 1}."); + } + + var multiplied = vectorizedValue / vector; + ref double currentDst = ref Unsafe.Add(ref dstRef, i); + Span dstWindow = MemoryMarshal.CreateSpan(ref currentDst, vectorSize); + multiplied.CopyTo(dstWindow); + } + + // Хвостовой цикл + for (; i < len; i++) + { + double divisor = Unsafe.Add(ref srcRef, i); + if (divisor == 0.0) + { + throw new DivideByZeroException($"Обнаружен делитель, равный нулю, в индексе {i}."); + } + Unsafe.Add(ref dstRef, i) = dividend / divisor; + } + } + internal static void PlusCore(ReadOnlySpan source, double summand, Span destination) + { + int len = source.Length; + ReadOnlySpan srcDouble = MemoryMarshal.Cast(source); + Span dstDouble = MemoryMarshal.Cast(destination); + + var vectorizedValue = new Vector(summand); + int vectorSize = Vector.Count; + int i = 0; + + ref double srcRef = ref MemoryMarshal.GetReference(srcDouble); + ref double dstRef = ref MemoryMarshal.GetReference(dstDouble); + + int SIMDEnd = len - (len % vectorSize); + for (; i < SIMDEnd; i += vectorSize) + { + ref double currentSrc = ref Unsafe.Add(ref srcRef, i); + ReadOnlySpan srcWindow = MemoryMarshal.CreateReadOnlySpan(ref currentSrc, vectorSize); + var vector = new Vector(srcWindow); + var multiplied = vector + vectorizedValue; + ref double currentDst = ref Unsafe.Add(ref dstRef, i); + Span dstWindow = MemoryMarshal.CreateSpan(ref currentDst, vectorSize); + multiplied.CopyTo(dstWindow); + } + + for (; i < len; i++) + { + Unsafe.Add(ref dstRef, i) = Unsafe.Add(ref srcRef, i) + summand; + } + } + internal static void MinusCore(ReadOnlySpan source, double subtrahend, Span destination) + { + int len = source.Length; + ReadOnlySpan srcDouble = MemoryMarshal.Cast(source); + Span dstDouble = MemoryMarshal.Cast(destination); + + var vectorizedValue = new Vector(subtrahend); + int vectorSize = Vector.Count; + int i = 0; + + ref double srcRef = ref MemoryMarshal.GetReference(srcDouble); + ref double dstRef = ref MemoryMarshal.GetReference(dstDouble); + + int SIMDEnd = len - (len % vectorSize); + for (; i < SIMDEnd; i += vectorSize) + { + ref double currentSrc = ref Unsafe.Add(ref srcRef, i); + ReadOnlySpan srcWindow = MemoryMarshal.CreateReadOnlySpan(ref currentSrc, vectorSize); + var vector = new Vector(srcWindow); + var multiplied = vector - vectorizedValue; + ref double currentDst = ref Unsafe.Add(ref dstRef, i); + Span dstWindow = MemoryMarshal.CreateSpan(ref currentDst, vectorSize); + multiplied.CopyTo(dstWindow); + } + + for (; i < len; i++) + { + Unsafe.Add(ref dstRef, i) = Unsafe.Add(ref srcRef, i) - subtrahend; + } + } + internal static void MinusCore(double minuend, ReadOnlySpan source, Span destination) + { + int len = source.Length; + ReadOnlySpan srcDouble = MemoryMarshal.Cast(source); + Span dstDouble = MemoryMarshal.Cast(destination); + + var vectorizedValue = new Vector(minuend); + int vectorSize = Vector.Count; + int i = 0; + + ref double srcRef = ref MemoryMarshal.GetReference(srcDouble); + ref double dstRef = ref MemoryMarshal.GetReference(dstDouble); + + int SIMDEnd = len - (len % vectorSize); + for (; i < SIMDEnd; i += vectorSize) + { + ref double currentSrc = ref Unsafe.Add(ref srcRef, i); + ReadOnlySpan srcWindow = MemoryMarshal.CreateReadOnlySpan(ref currentSrc, vectorSize); + var vector = new Vector(srcWindow); + var multiplied = vectorizedValue - vector; + ref double currentDst = ref Unsafe.Add(ref dstRef, i); + Span dstWindow = MemoryMarshal.CreateSpan(ref currentDst, vectorSize); + multiplied.CopyTo(dstWindow); + } + + for (; i < len; i++) + { + Unsafe.Add(ref dstRef, i) = minuend - Unsafe.Add(ref srcRef, i); + } + } + + [MethodImpl(MethodImplOptions.AggressiveOptimization)] + internal static void PowCore(ReadOnlySpan 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.MinValue, но для степеней это редчайший кейс + } + + var result = Vector.One; + var currentBase = baseVector; + + while (exp > 0) + { + if ((exp & 1) == 1) + { + result *= currentBase; + } + currentBase *= currentBase; + exp >>= 1; + } + + return result; + } + + for (; i < len; i++) + Unsafe.Add(ref dstRef, i) = Math.Pow(Unsafe.Add(ref srcRef, i), power); + } + [MethodImpl(MethodImplOptions.AggressiveOptimization)] + internal static unsafe void PowCore(ReadOnlySpan 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)] + private static double QuickPow(double baseValue, int exp) + { + // Разворачиваем самые частые степени в прямое умножение вообще без циклов и if! + switch (exp) + { + case 0: return 1.0; + case 1: return baseValue; + case 2: return baseValue * baseValue; + case 3: return baseValue * baseValue * baseValue; + case 4: { double x2 = baseValue * baseValue; return x2 * x2; } + } + + // Если степень отрицательная + if (exp < 0) + { + baseValue = 1.0 / baseValue; + if (exp == int.MinValue) return (1.0 / baseValue) * QuickPow(baseValue, int.MaxValue); + exp = -exp; + } + + // Тяжелая артиллерия для редких больших степеней + if (exp > 32) return Math.Pow(baseValue, exp); + + // Стандартный бинарный алгоритм для средних степеней (от 5 до 32) + double result = 1.0; + double currentBase = baseValue; + while (exp > 0) + { + if ((exp & 1) == 1) result *= currentBase; + currentBase *= currentBase; + exp >>= 1; + } + return result; + } + 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 Length[] Multiply(this Length[] units, double multiplicator) + { + if (units is null) return null!; + if (units.Length == 0) return []; + + var result = new Length[units.Length]; + MultiplyCore(units, multiplicator, result); + return result; + } + public static Length[] Multiply(this double multiplicator, Length[] units) + => units.Multiply(multiplicator); + + public static Length?[] Multiply(this Length?[] units, double multiplicator) + { + if (units is null) return []; + if (units.Length == 0) return []; + + var result = (Length?[])units.Clone(); + int len = result.Length; + for (int i = 0; i < len; i++) + { + ref var item = ref result[i]; + if (item.HasValue) item = new Length(item.Value._Value * multiplicator); + } + return result; + } + public static Length?[] Multiply(this double multiplicator, Length?[] units) + => units.Multiply(multiplicator); + + // === ReadOnlySpan === + public static Span Multiply(this ReadOnlySpan units, double multiplicator) + { + if (units.Length == 0) return []; + Span result = new Length[units.Length]; + LengthExtension.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 Length?[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 Length(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); + LengthExtension.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 []; + 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 Length(item.Value._Value * multiplicator); + } + return result; + } + public static List Multiply(this double multiplicator, List units) + => units.Multiply(multiplicator); + + // ========================================== + // === DIVIDE === + // ========================================== + + public static Length[] Divide(this Length[] 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 Length[units.Length]; + DivideCore(units, divisor, result); + return result; + } + public static Length[] Divide(this double dividend, Length[] units) + { + if (units is null) return null!; + if (units.Length == 0) return []; + + var result = new Length[units.Length]; + DivideCore(dividend, units, result); // Вызов зеркального ядра (число / массив) + return result; + } + public static Length?[] Divide(this Length?[] units, double divisor) + { + if (units is null) return []; + if (units.Length == 0) return []; + if (divisor == 0 || double.IsNaN(divisor)) throw new ArgumentException("Делитель не может быть 0 или NaN.", nameof(divisor)); + + var result = (Length?[])units.Clone(); + int len = result.Length; + for (int i = 0; i < len; i++) + { + ref var item = ref result[i]; + if (item.HasValue) item = new Length(item.Value._Value / divisor); + } + return result; + } + public static Length?[] Divide(this double dividend, Length?[] units) + { + if (units is null) return []; + if (units.Length == 0) return []; + + var result = (Length?[])units.Clone(); + int len = result.Length; + for (int i = 0; i < len; i++) + { + ref var item = ref result[i]; + if (item.HasValue) item = new Length(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 Length[units.Length]; + LengthExtension.DivideCore(units, divisor, result); + return result; + } + public static Span Divide(this double dividend, ReadOnlySpan units) + { + if (units.Length == 0) return []; + var result = new Length[units.Length]; + LengthExtension.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 Length?[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 Length(item.Value._Value / divisor); + } + return result; + } + public static Span Divide(this double dividend, ReadOnlySpan units) + { + if (units.Length == 0) return []; + Span result = new Length?[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 Length(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); + LengthExtension.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); + LengthExtension.DivideCore(dividend, CollectionsMarshal.AsSpan(units), CollectionsMarshal.AsSpan(result)); + return result; + } + public static List Divide(this List units, double divisor) + { + if (units is null) return []; + 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 Length(item.Value._Value / divisor); + } + return result; + } + public static List Divide(this double dividend, List units) + { + if (units is null) return []; + 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 Length(dividend / item.Value._Value); + } + return result; + } + + // ========================================== + // === PLUS === + // ========================================== + + public static Length[] Plus(this Length[] units, double summand) + { + if (units is null) return null!; + if (units.Length == 0) return []; + + var result = new Length[units.Length]; + PlusCore(units, summand, result); + return result; + } + public static Length[] Plus(this double summand, Length[] units) + => units.Plus(summand); + public static Length?[] Plus(this Length?[] units, double summand) + { + if (units is null) return []; + if (units.Length == 0) return []; + + var result = (Length?[])units.Clone(); + int len = result.Length; + for (int i = 0; i < len; i++) + { + ref var item = ref result[i]; + if (item.HasValue) item = new Length(item.Value._Value + summand); + } + return result; + } + public static Length?[] Plus(this double summand, Length?[] units) + => units.Plus(summand); + + // === ReadOnlySpan === + public static Span Plus(this ReadOnlySpan units, double summand) + { + if (units.Length == 0) return []; + var result = new Length[units.Length]; + LengthExtension.PlusCore(units, summand, result); + return result; + } + public static Span Plus(this double summand, ReadOnlySpan units) + => units.Plus(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); + LengthExtension.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 []; + 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 Length(item.Value._Value + summand); + } + return result; + } + public static List Plus(this double summand, List units) + => units.Plus(summand); + + // ========================================== + // === MINUS === + // ========================================== + + public static Length[] Minus(this Length[] units, double subtrahend) + { + if (units is null) return null!; + if (units.Length == 0) return []; + + var result = new Length[units.Length]; + MinusCore(units, subtrahend, result); + return result; + } + public static Length[] Minus(this double minuend, Length[] units) + { + if (units is null) return null!; + if (units.Length == 0) return []; + + var result = new Length[units.Length]; + MinusCore(minuend, units, result); // Вызов зеркального ядра (число - массив) + return result; + } + public static Length?[] Minus(this Length?[] units, double subtrahend) + { + if (units is null) return []; + if (units.Length == 0) return []; + + var result = (Length?[])units.Clone(); + int len = result.Length; + for (int i = 0; i < len; i++) + { + ref var item = ref result[i]; + if (item.HasValue) item = new Length(item.Value._Value - subtrahend); + } + return result; + } + public static Length?[] Minus(this double minuend, Length?[] units) + { + if (units is null) return []; + if (units.Length == 0) return []; + + var result = (Length?[])units.Clone(); + int len = result.Length; + for (int i = 0; i < len; i++) + { + ref var item = ref result[i]; + if (item.HasValue) item = new Length(minuend - item.Value._Value); + } + return result; + } + + // === ReadOnlySpan === + public static Span Minus(this ReadOnlySpan units, double subtrahend) + { + if (units.Length == 0) return []; + var result = new Length[units.Length]; + LengthExtension.MinusCore(units, subtrahend, result); + return result; + } + public static Span Minus(this double minuend, ReadOnlySpan units) + { + if (units.Length == 0) return []; + var result = new Length[units.Length]; + LengthExtension.MinusCore(minuend, units, result); + 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); + LengthExtension.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); + LengthExtension.MinusCore(minuend, CollectionsMarshal.AsSpan(units), CollectionsMarshal.AsSpan(result)); + return result; + } + public static List Minus(this List units, double subtrahend) + { + if (units is null) return []; + 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 Length(item.Value._Value - subtrahend); + } + return result; + } + public static List Minus(this double minuend, List units) + { + if (units is null) return []; + 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 Length(minuend - item.Value._Value); + } + return result; + } + + // ========================================== + // ЦЕЛАЯ СТЕПЕНЬ (int power) + // ========================================== + + public static Length[] Pow(this Length[] units, int power) + { + if (units is null) return null!; + if (units.Length == 0) return []; + + var result = new Length[units.Length]; + LengthExtension.PowCore(units, power, result); + return result; + } + public static Length?[] Pow(this Length?[] units, int power) + { + if (units is null) return []; + if (units.Length == 0) return []; + + var result = (Length?[])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 Length(QuickPow(item.Value._Value, power)); + } + } + return result; + } + + // === ReadOnlySpan === + public static Span Pow(this ReadOnlySpan units, int power) + { + if (units.Length == 0) return []; + var result = new Length[units.Length]; + LengthExtension.PowCore(units, power, result); + 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); + LengthExtension.PowCore(CollectionsMarshal.AsSpan(units), power, CollectionsMarshal.AsSpan(result)); + return result; + } + public static List Pow(this List units, int power) + { + if (units is null) return []; + 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 Length(QuickPow(item.Value._Value, power)); + } + return result; + } + + + // ========================================== + // ДРОБНАЯ СТЕПЕНЬ (double power) + // ========================================== + + public static Length[] Pow(this Length[] units, double power) + { + if (units is null) return null!; + if (units.Length == 0) return []; + + var result = new Length[units.Length]; + LengthExtension.PowCore(units, power, result); + return result; + } + public static Length?[] Pow(this Length?[] units, double power) + { + if (units is null) return []; + if (units.Length == 0) return []; + + var result = (Length?[])units.Clone(); + int len = result.Length; + for (int i = 0; i < len; i++) + { + ref var item = ref result[i]; + if (item.HasValue) + { + item = new Length(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 Length[units.Length]; + LengthExtension.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 Length?[units.Length]; + units.CopyTo(result); + for (int i = 0; i < len; i++) + { + ref var item = ref result[i]; + if (item.HasValue) item = new Length(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); + LengthExtension.PowCore(CollectionsMarshal.AsSpan(units), power, CollectionsMarshal.AsSpan(result)); + return result; + } + public static List Pow(this List units, double power) + { + if (units is null) return []; + 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 Length(Math.Pow(item.Value._Value, power)); + } + return result; + } + + + // ========================================== + // КВАДРАТНЫЙ КОРЕНЬ (Sqrt) + // ========================================== + + public static Length[] Sqrt(this Length[] units) + { + if (units is null) return null!; + if (units.Length == 0) return []; + + var result = new Length[units.Length]; + LengthExtension.SqrtCore(units, result); + return result; + } + public static Length?[] Sqrt(this Length?[] units) + { + if (units is null) return []; + if (units.Length == 0) return []; + + var result = (Length?[])units.Clone(); + int len = result.Length; + for (int i = 0; i < len; i++) + { + ref var item = ref result[i]; + if (item.HasValue) + { + item = new Length(Math.Sqrt(item.Value._Value)); + } + } + return result; + } + + // === ReadOnlySpan === + public static Span Sqrt(this ReadOnlySpan units) + { + if (units.Length == 0) return []; + var result = new Length[units.Length]; + LengthExtension.SqrtCore(units, result); + 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); + LengthExtension.SqrtCore(CollectionsMarshal.AsSpan(units), CollectionsMarshal.AsSpan(result)); + return result; + } + public static List Sqrt(this List units) + { + if (units is null) return []; + 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 Length(Math.Sqrt(item.Value._Value)); + } + return result; + } +} + +internal readonly struct LengthConv +{ + private LengthConv(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 LengthConv MilliMeters { get; } = new(1); + public static LengthConv CentiMeters { get; } = new(10); + public static LengthConv DeciMeters { get; } = new(100); + public static LengthConv Meters { get; } = new(1000); + public static LengthConv KiloMeters { get; } = new(1000000); +} \ No newline at end of file diff --git a/QWERTYkez.Mensura/globals.cs b/QWERTYkez.Mensura/globals.cs new file mode 100644 index 0000000..a077678 --- /dev/null +++ b/QWERTYkez.Mensura/globals.cs @@ -0,0 +1,12 @@ +global using System.Collections; +global using System.Collections.ObjectModel; +global using System.Collections.Specialized; +global using System.ComponentModel; +global using System.ComponentModel.DataAnnotations; +global using System.ComponentModel.DataAnnotations.Schema; +global using System.Diagnostics; +global using System.Numerics; +global using System.Runtime.CompilerServices; +global using System.Runtime.InteropServices; +global using System.Text.Json; +global using System.Text.Json.Serialization; \ No newline at end of file