From 664ba0fab7d888a6dc810d070b448a0528b6a161 Mon Sep 17 00:00:00 2001 From: That-One-Nerd Date: Mon, 12 May 2025 08:53:42 -0400 Subject: [PATCH] Finally got around to finishing up complex numbers. Sorry it took so long. --- Nerd_STF/Mathematics/Float2.cs | 1 + Nerd_STF/Mathematics/Int2.cs | 2 + Nerd_STF/Mathematics/Numbers/Complex.cs | 455 +++++++++++++++++++++++ Nerd_STF/Mathematics/Numbers/Fraction.cs | 28 +- Nerd_STF/Nerd_STF.csproj | 56 ++- 5 files changed, 501 insertions(+), 41 deletions(-) create mode 100644 Nerd_STF/Mathematics/Numbers/Complex.cs diff --git a/Nerd_STF/Mathematics/Float2.cs b/Nerd_STF/Mathematics/Float2.cs index 2528de4..12d8c1d 100644 --- a/Nerd_STF/Mathematics/Float2.cs +++ b/Nerd_STF/Mathematics/Float2.cs @@ -286,6 +286,7 @@ namespace Nerd_STF.Mathematics public static bool operator ==(Float2 a, Float2 b) => a.Equals(b); public static bool operator !=(Float2 a, Float2 b) => !a.Equals(b); + public static explicit operator Float2(Complex complex) => new Float2(complex.Real, complex.Imaginary); public static explicit operator Float2(Float3 floats) => new Float2(floats.x, floats.y); public static explicit operator Float2(Float4 floats) => new Float2(floats.x, floats.y); public static implicit operator Float2(Int2 ints) => new Float2(ints.x, ints.y); diff --git a/Nerd_STF/Mathematics/Int2.cs b/Nerd_STF/Mathematics/Int2.cs index 1ad10d0..2e9446f 100644 --- a/Nerd_STF/Mathematics/Int2.cs +++ b/Nerd_STF/Mathematics/Int2.cs @@ -1,5 +1,6 @@ using Nerd_STF.Exceptions; using Nerd_STF.Mathematics.Algebra; +using Nerd_STF.Mathematics.Numbers; using System; using System.Collections; using System.Collections.Generic; @@ -259,6 +260,7 @@ namespace Nerd_STF.Mathematics public static bool operator ==(Int2 a, Int2 b) => a.Equals(b); public static bool operator !=(Int2 a, Int2 b) => !a.Equals(b); + public static explicit operator Int2(Complex complex) => new Int2((int)complex.Real, (int)complex.Imaginary); public static explicit operator Int2(Float2 floats) => new Int2((int)floats.x, (int)floats.y); public static explicit operator Int2(Float3 floats) => new Int2((int)floats.x, (int)floats.y); public static explicit operator Int2(Float4 floats) => new Int2((int)floats.x, (int)floats.y); diff --git a/Nerd_STF/Mathematics/Numbers/Complex.cs b/Nerd_STF/Mathematics/Numbers/Complex.cs new file mode 100644 index 0000000..b4b97f5 --- /dev/null +++ b/Nerd_STF/Mathematics/Numbers/Complex.cs @@ -0,0 +1,455 @@ +using Nerd_STF.Exceptions; +using Nerd_STF.Helpers; +using Nerd_STF.Mathematics.Algebra; +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Numerics; +using System.Text; + +namespace Nerd_STF.Mathematics.Numbers +{ + public struct Complex : IEquatable, + IFormattable +#if CS11_OR_GREATER + ,INumber, + IInterpolable, + IPresets2d, + IRoundable, + ISimpleMathOperations, + ISplittable, + IVectorOperations +#endif + { + public static Complex Down => new Complex(0, 1); + public static Complex Left => new Complex(-1, 0); + public static Complex Right => new Complex(1, 0); + public static Complex Up => new Complex(0, -1); + + public static Complex One => new Complex(1, 0); + public static Complex Zero => new Complex(0, 0); + + public static Complex NaN => new Complex(double.NaN, double.NaN); + + public Complex Conjugate => new Complex(r, -i); + public double Magnitude => MathE.Sqrt(r * r + i * i); + public double MagnitudeSqr => r * r + i * i; + + public double Real + { + get => r; + set => r = value; + } + public double Imaginary + { + get => i; + set => i = value; + } + + public double r, i; + + public Complex(double real, double imaginary) + { + r = real; + i = imaginary; + } + +#if CS8_OR_GREATER + public static Complex Parse(string? str) => +#else + public static Complex Parse(string str) => +#endif + str is null ? NaN : Parse(str.AsSpan()); + public static Complex Parse(ReadOnlySpan str) + { + if (TryParse(str, out Complex result)) return result; + else throw new FormatException("Cannot parse complex number from input."); + } +#if CS8_OR_GREATER + public static bool TryParse(string? str, out Complex frac) => +#else + public static bool TryParse(string str, out Complex frac) => +#endif + TryParse(str.AsSpan(), out frac); + public static bool TryParse(ReadOnlySpan str, out Complex num) + { + if (str.Length == 0) + { + num = NaN; + return false; + } + + str = str.Trim(); + int signFirst, signSecond; + + if (str.StartsWith("+".AsSpan())) + { + signFirst = 1; + str = str.Slice(1); + } + else if (str.StartsWith("-".AsSpan())) + { + signFirst = -1; + str = str.Slice(1); + } + else signFirst = 1; + + ReadOnlySpan first, second; + int splitIndex = str.IndexOf('+'); + if (splitIndex == -1) splitIndex = str.IndexOf('-'); + + if (splitIndex != -1) + { + first = str; + second = ReadOnlySpan.Empty; + } + else + { + first = str.Slice(0, splitIndex); + second = str.Slice(splitIndex); + } + first = first.Trim(); + second = second.Trim(); + + if (second.StartsWith("+".AsSpan())) + { + signSecond = 1; + second = second.Slice(1).Trim(); + } + else if (str.StartsWith("-".AsSpan())) + { + signSecond = -1; + second = second.Slice(1).Trim(); + } + else signSecond = 1; + + bool firstIsImag; + if (first.EndsWith("i".AsSpan())) + { + firstIsImag = true; + first = first.Slice(0, first.Length - 1).Trim(); + } + else if (second.EndsWith("i".AsSpan())) + { + firstIsImag = false; + second = first.Slice(0, second.Length - 1).Trim(); + } + else + { + num = NaN; + return false; + } + + double firstNum = ParseHelper.ParseDouble(first) * signFirst, + secondNum = ParseHelper.ParseDouble(second) * signSecond; + + if (firstIsImag) num = new Complex(secondNum, firstNum); + else num = new Complex(firstNum, secondNum); + + return true; + } + + public static Complex Abs(Complex num) => new Complex(num.Magnitude, 0); + public static Complex Ceiling(Complex num) => + new Complex(MathE.Ceiling(num.r), + MathE.Ceiling(num.i)); + public static Complex Clamp(Complex num, Complex min, Complex max) => + new Complex(MathE.Clamp(num.r, min.r, max.r), + MathE.Clamp(num.i, min.i, max.i)); + public static Complex ClampMagnitude(Complex num, double minMag, double maxMag) + { + Complex copy = num; + ClampMagnitude(ref copy, minMag, maxMag); + return copy; + } + public static void ClampMagnitude(ref Complex num, double minMag, double maxMag) + { + if (minMag > maxMag) throw new ClampOrderMismatchException(nameof(minMag), nameof(maxMag)); + double mag = num.Magnitude; + + if (mag < minMag) + { + double factor = minMag / mag; + num.r *= factor; + num.i *= factor; + } + else if (mag > maxMag) + { + double factor = maxMag / mag; + num.r *= factor; + num.i *= factor; + } + } + public static double Dot(Complex a, Complex b) => a.r * b.r + a.i * b.i; + public static double Dot(IEnumerable values) + { + double r = 1, i = 1; + foreach (Complex val in values) + { + r *= val.r; + i *= val.i; + } + return r + i; + } + public static Complex Floor(Complex num) => + new Complex(MathE.Floor(num.r), + MathE.Floor(num.i)); + public static Complex Lerp(Complex a, Complex b, double t, bool clamp = false) => + new Complex(MathE.Lerp(a.r, b.r, t, clamp), + MathE.Lerp(a.i, b.i, t, clamp)); + public static Complex Product(IEnumerable vals) + { + bool any = false; + double resultR = 1, resultI = 1; + foreach (Complex val in vals) + { + any = true; + resultR *= val.r; + resultI *= val.i; + } + return any ? new Complex(resultR, resultI) : Zero; + } + public static Complex Round(Complex val) => + new Complex(MathE.Round(val.r), + MathE.Round(val.i)); + public static Complex Sum(IEnumerable vals) + { + double resultR = 0; + double resultI = 0; + foreach (Complex val in vals) + { + resultR += val.r; + resultI += val.i; + } + return new Complex(resultR, resultI); + } + +#if CS8_OR_GREATER + private static bool TryConvertFrom(object? value, out Complex result) +#else + private static bool TryConvertFrom(object value, out Complex result) +#endif + { + if (value is Complex vComplex) result = vComplex; + else if (value is Fraction vFrac) result = new Complex(vFrac.GetValue(), 0); + else if (value is Float2 vFloat2) result = new Complex(vFloat2.x, vFloat2.y); + else if (value is Vector2 vVector2) result = new Complex(vVector2.X, vVector2.Y); + else if (value is Int2 vInt2) result = new Complex(vInt2.x, vInt2.y); + else if (value is double vDouble) result = new Complex(vDouble, 0); + else if (value is float vSingle) result = new Complex(vSingle, 0); +#if NET5_0_OR_GREATER + else if (value is Half vHalf) result = new Complex((double)vHalf, 0); +#endif + else if (value is int vInt32) result = new Complex(vInt32, 0); + else + { + result = new Complex(0, 0); + return false; + } + return true; + } + + public static bool IsEvenInteger(Complex value) => value.i == 0 && value.r % 2 == 0; + public static bool IsOddInteger(Complex value) => value.i == 0 && value.r % 2 == 1; + public static bool IsFinite(Complex value) => TargetHelper.IsFinite(value.r) && + TargetHelper.IsFinite(value.i); + public static bool IsInfinity(Complex value) => TargetHelper.IsInfinity(value.r) || + TargetHelper.IsInfinity(value.i); + public static bool IsInteger(Complex value) => value.i == 0 && value.r % 1 == 0; + public static bool IsNaN(Complex value) + { + // NaN never equals itself. +#pragma warning disable CS1718 + return (value.r != value.r) || + (value.i != value.i); +#pragma warning restore CS1718 + } + public static bool IsNegative(Complex value) => value.r < 0; + public static bool IsNegativeInfinity(Complex value) => value.r == double.NegativeInfinity || + value.i == double.NegativeInfinity; + public static bool IsNormal(Complex value) => false; // ??? uhh i think this is right + public static bool IsPositive(Complex value) => value.r > 0; + public static bool IsPositiveInfinity(Complex value) => value.r == double.PositiveInfinity || + value.i == double.PositiveInfinity; + public static bool IsRealNumber(Complex value) => value.i == 0; + public static bool IsZero(Complex value) => value.r == 0 && value.i == 0; + public static Complex MaxMagnitude(Complex a, Complex b) => a.MagnitudeSqr > b.MagnitudeSqr ? a : b; + public static Complex MinMagnitude(Complex a, Complex b) => a.MagnitudeSqr < b.MagnitudeSqr ? a : b; +#if CS11_OR_GREATER + static Complex INumberBase.MaxMagnitudeNumber(Complex a, Complex b) => MaxMagnitude(a, b); + static Complex INumberBase.MinMagnitudeNumber(Complex a, Complex b) => MinMagnitude(a, b); + static bool INumberBase.IsCanonical(Complex value) => true; + static bool INumberBase.IsComplexNumber(Complex value) => value.i != 0; + static bool INumberBase.IsImaginaryNumber(Complex value) => value.i != 0; + static bool INumberBase.IsSubnormal(Complex value) => false; // What does this mean??? + + static Complex INumberBase.Parse(string str, NumberStyles numStyles, IFormatProvider? provider) => Parse(str); + static Complex INumberBase.Parse(ReadOnlySpan str, NumberStyles numStyles, IFormatProvider? provider) => Parse(str); + static Complex IParsable.Parse(string str, IFormatProvider? provider) => Parse(str); + static Complex ISpanParsable.Parse(ReadOnlySpan str, IFormatProvider? provider) => Parse(str); + static bool INumberBase.TryParse(string? str, NumberStyles numStyles, IFormatProvider? provider, out Complex num) => TryParse(str, out num); + static bool INumberBase.TryParse(ReadOnlySpan str, NumberStyles numStyles, IFormatProvider? provider, out Complex num) => TryParse(str, out num); + static bool IParsable.TryParse(string? str, IFormatProvider? provider, out Complex num) => TryParse(str, out num); + static bool ISpanParsable.TryParse(ReadOnlySpan str, IFormatProvider? provider, out Complex num) => TryParse(str, out num); + + static Complex IAdditiveIdentity.AdditiveIdentity => Zero; + static Complex IMultiplicativeIdentity.MultiplicativeIdentity => One; + static int INumberBase.Radix => 2; // Not super sure what to put here. + + private static bool TryConvertTo(Complex num, out T result) + { + object? tempValue; + + if (typeof(T) == typeof(Complex)) tempValue = num; + else if (typeof(T) == typeof(Fraction)) tempValue = Fraction.Approximate(num.r); + else if (typeof(T) == typeof(Float2)) tempValue = new Float2(num.r, num.i); + else if (typeof(T) == typeof(Vector2)) tempValue = new Vector2((float)num.r, (float)num.i); + else if (typeof(T) == typeof(Int2)) tempValue = new Int2((int)num.r, (int)num.i); + else if (typeof(T) == typeof(double)) tempValue = num.r; + else if (typeof(T) == typeof(float)) tempValue = (float)num.r; +#if NET5_0_OR_GREATER + else if (typeof(T) == typeof(Half)) tempValue = (Half)num.r; +#endif + else if (typeof(T) == typeof(int)) tempValue = (int)num.r; + else + { + result = default!; + return false; + } + + result = (T)tempValue; + return true; + } + static bool INumberBase.TryConvertFromChecked(TOther value, out Complex result) => TryConvertFrom(value, out result); + static bool INumberBase.TryConvertFromSaturating(TOther value, out Complex result) => TryConvertFrom(value, out result); + static bool INumberBase.TryConvertFromTruncating(TOther value, out Complex result) => TryConvertFrom(value, out result); + static bool INumberBase.TryConvertToChecked(Complex value, out TOther result) => TryConvertTo(value, out result); + static bool INumberBase.TryConvertToSaturating(Complex value, out TOther result) => TryConvertTo(value, out result); + static bool INumberBase.TryConvertToTruncating(Complex value, out TOther result) => TryConvertTo(value, out result); +#endif + + public static (double[] reals, double[] imaginaries) SplitArray(IEnumerable vals) + { + int count = vals.Count(); + double[] reals = new double[count], imaginaries = new double[count]; + int index = 0; + foreach (Complex val in vals) + { + reals[index] = val.r; + imaginaries[index] = val.i; + } + return (reals, imaginaries); + } + + public int CompareTo(double other) => Magnitude.CompareTo(MathE.Abs(other)); + public int CompareTo(Complex other) => Magnitude.CompareTo(other.Magnitude); +#if CS8_OR_GREATER + public int CompareTo(object? other) +#else + public int CompareTo(object other) +#endif + { + if (other is null) return 1; + else if (other is Complex otherComplex) return CompareTo(otherComplex); + else if (TryConvertFrom(other, out Complex otherConvert)) return CompareTo(otherConvert); + else return 0; + } + public bool Equals(double other) => r == other && i == 0; + public bool Equals(Complex other) => r == other.r && i == other.i; +#if CS8_OR_GREATER + public override bool Equals(object? other) +#else + public override bool Equals(object other) +#endif + { + if (other is null) return false; + else if (other is Complex otherComplex) return Equals(otherComplex); + else if (TryConvertFrom(other, out Complex otherConvert)) return Equals(otherConvert); + else return false; + } + public override int GetHashCode() => base.GetHashCode(); + public override string ToString() => ToString(null, null); +#if CS8_OR_GREATER + public string ToString(string? format) => ToString(format, null); + public string ToString(IFormatProvider? provider) => ToString(null, provider); +#else + public string ToString(string format) => ToString(format, null); + public string ToString(IFormatProvider provider) => ToString(null, provider); +#endif +#if CS8_OR_GREATER + public string ToString(string? format, IFormatProvider? provider) +#else + public string ToString(string format, IFormatProvider provider) +#endif + { + if (r == 0 && i == 0) return 0.0.ToString(format, provider); + else if (r == 0) return $"{i.ToString(format, provider)}i"; + else + { + StringBuilder builder = new StringBuilder(); + builder.Append(r.ToString(format, provider)); + if (i > 0) + { + builder.Append(" + "); + builder.Append(i.ToString(format, provider)); + builder.Append('i'); + } + else if (i < 0) + { + builder.Append(" - "); + builder.Append((-i).ToString(format, provider)); + builder.Append('i'); + } + return builder.ToString(); + } + } + +#if CS11_OR_GREATER + public bool TryFormat(Span dest, out int charsWritten, ReadOnlySpan format, IFormatProvider? provider) + { + // Not really great, but I don't want to do this right now. + string result = ToString(format.ToString(), provider); + result.CopyTo(dest); + charsWritten = result.Length; + return true; + } +#endif + + public static Complex operator +(Complex a) => a; + public static Complex operator +(Complex a, Complex b) => new Complex(a.r + b.r, a.i + b.i); + public static Complex operator +(Complex a, double b) => new Complex(a.r + b, a.i); + public static Complex operator ++(Complex a) => new Complex(a.r + 1, a.i); + public static Complex operator -(Complex a) => new Complex(-a.r, -a.i); + public static Complex operator -(Complex a, Complex b) => new Complex(a.r - b.r, a.i - b.i); + public static Complex operator -(Complex a, double b) => new Complex(a.r - b, a.i); + public static Complex operator --(Complex a) => new Complex(a.r - 1, a.i); + public static Complex operator *(Complex a, Complex b) => new Complex(a.r * b.r - a.i * b.i, a.r * b.i + a.i * b.r); + public static Complex operator *(Complex a, double b) => new Complex(a.r * b, a.i * b); + public static Complex operator /(Complex a, Complex b) + { + double scaleFactor = 1 / (b.r * b.r + b.i * b.i); + return new Complex((a.r * b.r + a.i * b.i) * scaleFactor, + (a.i * b.r - a.r * b.i) * scaleFactor); + } + public static Complex operator /(Complex a, double b) => new Complex(a.r / b, a.i / b); + public static Complex operator %(Complex a, Complex b) + { + // TODO: Maybe expand and inline this. Don't feel like it at the moment. + return a + b * Ceiling(-a / b); + } + public static Complex operator %(Complex a, double b) => a % new Complex(b, 0); + public static bool operator ==(Complex a, Complex b) => a.Equals(b); + public static bool operator !=(Complex a, Complex b) => !a.Equals(b); + public static bool operator >(Complex a, Complex b) => a.CompareTo(b) > 0; + public static bool operator <(Complex a, Complex b) => a.CompareTo(b) < 0; + public static bool operator >=(Complex a, Complex b) => a.CompareTo(b) >= 0; + public static bool operator <=(Complex a, Complex b) => a.CompareTo(b) <= 0; + + public static implicit operator Complex(System.Numerics.Complex num) => new Complex(num.Real, num.Imaginary); + public static implicit operator System.Numerics.Complex(Complex num) => new Complex(num.r, num.i); + public static implicit operator Complex(Float2 group) => new Complex(group.x, group.y); + public static explicit operator Complex(Int2 group) => new Complex(group.x, group.y); + public static explicit operator Complex(Vector2 group) => new Complex(group.X, group.Y); + } +} diff --git a/Nerd_STF/Mathematics/Numbers/Fraction.cs b/Nerd_STF/Mathematics/Numbers/Fraction.cs index 3ee425f..c7c077d 100644 --- a/Nerd_STF/Mathematics/Numbers/Fraction.cs +++ b/Nerd_STF/Mathematics/Numbers/Fraction.cs @@ -325,25 +325,13 @@ namespace Nerd_STF.Mathematics.Numbers return false; } else if (value is Fraction valueFrac) result = valueFrac; + else if (value is Complex valueComp) result = Approximate(valueComp.Real); else if (value is double valueDouble) result = Approximate(valueDouble); else if (value is float valueSingle) result = Approximate(valueSingle); #if NET5_0_OR_GREATER else if (value is Half valueHalf) result = Approximate((double)valueHalf); #endif -#if NET7_0_OR_GREATER - else if (value is UInt128 valueUInt128) result = new Fraction((int)valueUInt128, 1); - else if (value is Int128 valueInt128) result = new Fraction((int)valueInt128, 1); -#endif - else if (value is ulong valueUInt64) result = new Fraction((int)valueUInt64, 1); - else if (value is long valueInt64) result = new Fraction((int)valueInt64, 1); - else if (value is uint valueUInt32) result = new Fraction((int)valueUInt32, 1); else if (value is int valueInt32) result = new Fraction(valueInt32, 1); - else if (value is ushort valueUInt16) result = new Fraction(valueUInt16, 1); - else if (value is short valueInt16) result = new Fraction(valueInt16, 1); - else if (value is byte valueUInt8) result = new Fraction(valueUInt8, 1); - else if (value is sbyte valueInt8) result = new Fraction(valueInt8, 1); - else if (value is IntPtr valueInt) result = new Fraction((int)valueInt, 1); - else if (value is UIntPtr valueUInt) result = new Fraction((int)valueUInt, 1); else { result = NaN; @@ -380,6 +368,7 @@ namespace Nerd_STF.Mathematics.Numbers static bool INumberBase.IsComplexNumber(Fraction val) => false; static bool INumberBase.IsImaginaryNumber(Fraction val) => false; static bool INumberBase.IsSubnormal(Fraction val) => false; // What does this mean??? + static Fraction INumberBase.Parse(string? str, NumberStyles style, IFormatProvider? provider) => Parse(str); static Fraction INumberBase.Parse(ReadOnlySpan str, NumberStyles style, IFormatProvider? provider) => Parse(str); static bool INumberBase.TryParse(string? str, NumberStyles style, IFormatProvider? provider, out Fraction frac) => TryParse(str, out frac); @@ -388,6 +377,7 @@ namespace Nerd_STF.Mathematics.Numbers static bool IParsable.TryParse(string? str, IFormatProvider? provider, out Fraction frac) => TryParse(str, out frac); static Fraction ISpanParsable.Parse(ReadOnlySpan str, IFormatProvider? provider) => Parse(str); static bool ISpanParsable.TryParse(ReadOnlySpan str, IFormatProvider? provider, out Fraction frac) => TryParse(str, out frac); + static Fraction IAdditiveIdentity.AdditiveIdentity => Zero; static Fraction IMultiplicativeIdentity.MultiplicativeIdentity => One; static int INumberBase.Radix => 2; // Not super sure what to put here. @@ -400,23 +390,13 @@ namespace Nerd_STF.Mathematics.Numbers object? tempValue; if (typeof(T) == typeof(Fraction)) tempValue = frac; + else if (typeof(T) == typeof(Complex)) tempValue = (frac.GetValue(), 0); else if (typeof(T) == typeof(double)) tempValue = frac.GetValue(); else if (typeof(T) == typeof(float)) tempValue = (float)frac.GetValue(); #if NET5_0_OR_GREATER else if (typeof(T) == typeof(Half)) tempValue = (Half)frac.GetValue(); #endif -#if NET7_0_OR_GREATER - else if (typeof(T) == typeof(UInt128)) tempValue = (UInt128)frac.GetValue(); - else if (typeof(T) == typeof(Int128)) tempValue = (Int128)frac.GetValue(); -#endif - else if (typeof(T) == typeof(ulong)) tempValue = (ulong)frac.GetValue(); - else if (typeof(T) == typeof(long)) tempValue = (long)frac.GetValue(); - else if (typeof(T) == typeof(uint)) tempValue = (uint)frac.GetValue(); else if (typeof(T) == typeof(int)) tempValue = (int)frac.GetValue(); - else if (typeof(T) == typeof(ushort)) tempValue = (ushort)frac.GetValue(); - else if (typeof(T) == typeof(short)) tempValue = (short)frac.GetValue(); - else if (typeof(T) == typeof(byte)) tempValue = (byte)frac.GetValue(); - else if (typeof(T) == typeof(sbyte)) tempValue = (sbyte)frac.GetValue(); else { value = default!; diff --git a/Nerd_STF/Nerd_STF.csproj b/Nerd_STF/Nerd_STF.csproj index 60215ec..a6ab39a 100644 --- a/Nerd_STF/Nerd_STF.csproj +++ b/Nerd_STF/Nerd_STF.csproj @@ -3,7 +3,7 @@ - netstandard1.1;netstandard1.3;netstandard2.1;netcoreapp3.0;net5.0;net7.0 + netstandard1.1;netstandard1.3;netstandard2.1;net46;net462;net47;netcoreapp3.0;net5.0;net7.0 true True portable @@ -93,7 +93,7 @@ I think the Image type will be completely reworked and might be what version 3.1 - + @@ -102,13 +102,48 @@ I think the Image type will be completely reworked and might be what version 3.1 - + + + + + + + + + + + + + + + - + + $(DefineConstants);CS7_OR_GREATER + + + + $(DefineConstants);CS7_OR_GREATER + + + + $(DefineConstants);CS7_OR_GREATER;CS8_OR_GREATER + enable + + + + $(DefineConstants);CS7_OR_GREATER + + + + $(DefineConstants);CS7_OR_GREATER + + + $(DefineConstants);CS7_OR_GREATER @@ -126,19 +161,6 @@ I think the Image type will be completely reworked and might be what version 3.1 $(DefineConstants);CS7_OR_GREATER;CS8_OR_GREATER;CS9_OR_GREATER;CS10_OR_GREATER;CS11_OR_GREATER enable - - - $(DefineConstants);CS7_OR_GREATER - - - - $(DefineConstants);CS7_OR_GREATER - - - - $(DefineConstants);CS7_OR_GREATER;CS8_OR_GREATER - enable -