using Newtonsoft.Json; using RhythmBase.Converters; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Numerics; namespace RhythmBase.Components { /// /// An Expression /// [JsonConverter(typeof(ExpressionConverter))] public struct RDExpression : INumber { /// /// Gets the numeric value of the expression. /// public float NumericValue { get; } /// /// Gets the expression value as a string. /// public readonly string ExpressionValue { get { bool isNumeric = IsNumeric; string ExpressionValue = isNumeric ? NumericValue.ToString() : _exp; return ExpressionValue; } } /// /// Gets the evaluated value of the expression. /// public readonly float Value => IsNumeric ? NumericValue : Calculate(ExpressionValue); private static float Calculate(string exp) { if (string.IsNullOrWhiteSpace(exp)) return 0; if (float.TryParse(exp, out float result)) return result; return 0; } static RDExpression INumberBase.One => 1; static int INumberBase.Radix => 10; /// /// Gets the additive identity for the type. /// public static RDExpression Zero => 0; static RDExpression IAdditiveIdentity.AdditiveIdentity => 0; static RDExpression IMultiplicativeIdentity.MultiplicativeIdentity => 1; /// /// Initializes a new instance of the struct with a numeric value. /// /// The numeric value of the expression. public RDExpression(float value) { this = default; IsNumeric = true; NumericValue = value; } /// /// Initializes a new instance of the struct with a string value. /// /// The string value of the expression. public RDExpression([AllowNull] string value) { IsNumeric = float.TryParse(value, out float numeric); if (IsNumeric) NumericValue = numeric; else _exp = value ?? ""; } /// public override readonly bool Equals([NotNullWhen(true)] object? obj) => obj is RDExpression e && Equals(e); /// public readonly bool Equals(RDExpression other) => (IsNumeric == other.IsNumeric && NumericValue == other.NumericValue) || _exp == other._exp; /// public override readonly int GetHashCode() { HashCode hash = default; hash.Add(ExpressionValue); return hash.ToHashCode(); } /// public override readonly string ToString() => ExpressionValue; /// /// Converts a string to a nullable RDExpression. /// /// The string to convert. /// A nullable RDExpression if the string is not null or empty; otherwise, null. public static RDExpression? Nullable(string s) => s != null && s.Length != 0 ? new RDExpression?(new RDExpression(s)) : null; readonly int IComparable.CompareTo(object? obj) { if (obj is RDExpression other) { return CompareTo(other); } throw new ArgumentException("Object is not a RDExpression"); } /// public readonly int CompareTo(RDExpression other) { if (IsNumeric && other.IsNumeric) { return NumericValue.CompareTo(other.NumericValue); } return string.Compare(ExpressionValue, other.ExpressionValue, StringComparison.Ordinal); } static RDExpression INumberBase.Abs(RDExpression value) { return value.IsNumeric ? new RDExpression(Math.Abs(value.NumericValue)) : value; } static bool INumberBase.IsCanonical(RDExpression value) { return true; } static bool INumberBase.IsComplexNumber(RDExpression value) { return false; } static bool INumberBase.IsEvenInteger(RDExpression value) { return value.IsNumeric && value.NumericValue % 2 == 0; } static bool INumberBase.IsFinite(RDExpression value) { return value.IsNumeric && !float.IsInfinity(value.NumericValue); } static bool INumberBase.IsImaginaryNumber(RDExpression value) { return false; } static bool INumberBase.IsInfinity(RDExpression value) { return value.IsNumeric && float.IsInfinity(value.NumericValue); } static bool INumberBase.IsInteger(RDExpression value) { return value.IsNumeric && value.NumericValue == Math.Floor(value.NumericValue); } static bool INumberBase.IsNaN(RDExpression value) { return value.IsNumeric && float.IsNaN(value.NumericValue); } static bool INumberBase.IsNegative(RDExpression value) { return value.IsNumeric ? value.NumericValue < 0 : Calculate(value.ExpressionValue) < 0; } static bool INumberBase.IsNegativeInfinity(RDExpression value) { return value.IsNumeric && float.IsNegativeInfinity(value.NumericValue); } static bool INumberBase.IsNormal(RDExpression value) { return value.IsNumeric && !float.IsSubnormal(value.NumericValue); } static bool INumberBase.IsOddInteger(RDExpression value) { return value.IsNumeric && value.NumericValue % 2 != 0; } static bool INumberBase.IsPositive(RDExpression value) { return value.IsNumeric ? value.NumericValue > 0 : Calculate(value.ExpressionValue) > 0; } static bool INumberBase.IsPositiveInfinity(RDExpression value) { return value.IsNumeric && float.IsPositiveInfinity(value.NumericValue); } static bool INumberBase.IsRealNumber(RDExpression value) { return value.IsNumeric; } static bool INumberBase.IsSubnormal(RDExpression value) { return value.IsNumeric && float.IsSubnormal(value.NumericValue); } static bool INumberBase.IsZero(RDExpression value) { return value.IsNumeric ? value.NumericValue == 0 : Calculate(value.ExpressionValue) == 0; } static RDExpression INumberBase.MaxMagnitude(RDExpression x, RDExpression y) { return x.IsNumeric && y.IsNumeric ? (Math.Abs(x.NumericValue) > Math.Abs(y.NumericValue) ? x : y) : throw new NotImplementedException(); } static RDExpression INumberBase.MaxMagnitudeNumber(RDExpression x, RDExpression y) { return x.IsNumeric && y.IsNumeric ? (Math.Abs(x.NumericValue) > Math.Abs(y.NumericValue) ? x : y) : throw new NotImplementedException(); } static RDExpression INumberBase.MinMagnitude(RDExpression x, RDExpression y) { return x.IsNumeric && y.IsNumeric ? (Math.Abs(x.NumericValue) < Math.Abs(y.NumericValue) ? x : y) : throw new NotImplementedException(); } static RDExpression INumberBase.MinMagnitudeNumber(RDExpression x, RDExpression y) { return x.IsNumeric && y.IsNumeric ? (Math.Abs(x.NumericValue) < Math.Abs(y.NumericValue) ? x : y) : throw new NotImplementedException(); } static RDExpression INumberBase.Parse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider) { if (float.TryParse(s, style, provider, out float result)) { return new RDExpression(result); } throw new FormatException("Input string was not in a correct format."); } static RDExpression INumberBase.Parse(string s, NumberStyles style, IFormatProvider? provider) { if (float.TryParse(s, style, provider, out float result)) { return new RDExpression(result); } throw new FormatException("Input string was not in a correct format."); } static bool INumberBase.TryConvertFromChecked(TOther value, out RDExpression result) { result = new RDExpression(); return false; } static bool INumberBase.TryConvertFromSaturating(TOther value, out RDExpression result) { result = new RDExpression(); return false; } static bool INumberBase.TryConvertFromTruncating(TOther value, out RDExpression result) { result = new RDExpression(); return false; } static bool INumberBase.TryConvertToChecked(RDExpression value, out TOther result) { result = default!; return false; } static bool INumberBase.TryConvertToSaturating(RDExpression value, out TOther result) { result = default!; return false; } static bool INumberBase.TryConvertToTruncating(RDExpression value, out TOther result) { result = default!; return false; } static bool INumberBase.TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider, out RDExpression result) { result = new(s.ToString()); return true; } static bool INumberBase.TryParse(string? s, NumberStyles style, IFormatProvider? provider, out RDExpression result) { result = new(s ?? "0"); return true; } readonly bool ISpanFormattable.TryFormat(Span destination, out int charsWritten, ReadOnlySpan format, IFormatProvider? provider) { return NumericValue.TryFormat(destination, out charsWritten, format, provider); } readonly string IFormattable.ToString(string? format, IFormatProvider? formatProvider) { return NumericValue.ToString(format, formatProvider); } static RDExpression ISpanParsable.Parse(ReadOnlySpan s, IFormatProvider? provider) { if (float.TryParse(s, NumberStyles.Float | NumberStyles.AllowThousands, provider, out float result)) { return new RDExpression(result); } throw new FormatException("Input string was not in a correct format."); } static bool ISpanParsable.TryParse(ReadOnlySpan s, IFormatProvider? provider, out RDExpression result) { if (float.TryParse(s, NumberStyles.Float | NumberStyles.AllowThousands, provider, out float numericResult)) { result = new RDExpression(numericResult); return true; } result = default; return false; } static RDExpression IParsable.Parse(string s, IFormatProvider? provider) { if (float.TryParse(s, NumberStyles.Float | NumberStyles.AllowThousands, provider, out float result)) { return new RDExpression(result); } throw new FormatException("Input string was not in a correct format."); } static bool IParsable.TryParse(string? s, IFormatProvider? provider, out RDExpression result) { if (float.TryParse(s, NumberStyles.Float | NumberStyles.AllowThousands, provider, out float numericResult)) { result = new RDExpression(numericResult); return true; } result = default; return false; } /// public static RDExpression operator +(RDExpression left, float right) => left.IsNumeric ? new RDExpression(left.NumericValue + right) : new RDExpression(string.Format("{0}+{1}", left.ExpressionValue, right)); /// public static RDExpression operator +(float left, RDExpression right) => right.IsNumeric ? new RDExpression(left + right.NumericValue) : new RDExpression(string.Format("{0}+{1}", left, right.ExpressionValue)); /// public static RDExpression operator +(RDExpression left, RDExpression right) => left.IsNumeric && right.IsNumeric ? new RDExpression(left.NumericValue + right.NumericValue) : new RDExpression(string.Format("{0}+{1}", left.ExpressionValue, right.ExpressionValue)); /// public static RDExpression operator -(RDExpression left, float right) => left.IsNumeric ? new RDExpression(left.NumericValue - right) : new RDExpression(string.Format("{0}-{1}", left.ExpressionValue, right)); /// public static RDExpression operator -(float left, RDExpression right) => right.IsNumeric ? new RDExpression(left - right.NumericValue) : new RDExpression(string.Format("{0}-{1}", left, right.ExpressionValue)); /// public static RDExpression operator -(RDExpression left, RDExpression right) => left.IsNumeric && right.IsNumeric ? new RDExpression(left.NumericValue - right.NumericValue) : new RDExpression(string.Format("{0}-{1}", left.ExpressionValue, right.ExpressionValue)); /// public static RDExpression operator *(RDExpression left, float right) => left.IsNumeric ? new RDExpression(left.NumericValue * right) : new RDExpression(string.Format("({0})*{1}", left.ExpressionValue, right)); /// public static RDExpression operator *(float left, RDExpression right) => right.IsNumeric ? new RDExpression(left * right.NumericValue) : new RDExpression(string.Format("{0}*({1})", left, right.ExpressionValue)); /// public static RDExpression operator *(RDExpression left, RDExpression right) => left.IsNumeric && right.IsNumeric ? new RDExpression(left.NumericValue * right.NumericValue) : new RDExpression(string.Format("({0})*({1})", left.ExpressionValue, right.ExpressionValue)); /// public static RDExpression operator /(RDExpression left, float right) => left.IsNumeric ? new RDExpression(left.NumericValue / right) : new RDExpression(string.Format("({0})/{1}", left.ExpressionValue, right)); /// public static RDExpression operator /(float left, RDExpression right) => right.IsNumeric ? new RDExpression(left / right.NumericValue) : new RDExpression(string.Format("{0}/({1})", left, right.ExpressionValue)); /// public static RDExpression operator /(RDExpression left, RDExpression right) => left.IsNumeric && right.IsNumeric ? new RDExpression(left.NumericValue / right.NumericValue) : new RDExpression(string.Format("({0})/({1})", left.ExpressionValue, right.ExpressionValue)); /// public static bool operator ==(RDExpression left, RDExpression right) => left.Equals(right); /// public static bool operator !=(RDExpression left, RDExpression right) => !(left == right); /// public static implicit operator RDExpression(float v) => new(v); /// public static implicit operator RDExpression(string v) => new(v); static bool IComparisonOperators.operator >(RDExpression left, RDExpression right) { return left.CompareTo(right) > 0; } static bool IComparisonOperators.operator >=(RDExpression left, RDExpression right) { return left.CompareTo(right) >= 0; } static bool IComparisonOperators.operator <(RDExpression left, RDExpression right) { return left.CompareTo(right) < 0; } static bool IComparisonOperators.operator <=(RDExpression left, RDExpression right) { return left.CompareTo(right) <= 0; } static RDExpression IModulusOperators.operator %(RDExpression left, RDExpression right) { if (left.IsNumeric && right.IsNumeric) { return new RDExpression(left.NumericValue % right.NumericValue); } throw new NotImplementedException("Modulus operator is not implemented for non-numeric expressions."); } static RDExpression IDecrementOperators.operator --(RDExpression value) { if (value.IsNumeric) { return new RDExpression(value.NumericValue - 1); } throw new NotImplementedException("Decrement operator is not implemented for non-numeric expressions."); } static RDExpression IIncrementOperators.operator ++(RDExpression value) { if (value.IsNumeric) { return new RDExpression(value.NumericValue + 1); } throw new NotImplementedException("Increment operator is not implemented for non-numeric expressions."); } static RDExpression IUnaryNegationOperators.operator -(RDExpression value) { if (value.IsNumeric) { return new RDExpression(-value.NumericValue); } throw new NotImplementedException("Unary negation operator is not implemented for non-numeric expressions."); } static RDExpression IUnaryPlusOperators.operator +(RDExpression value) { return value; } private readonly string _exp = ""; /// /// /// public bool IsNumeric { get; private set; } = false; } }