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;
}
}