| | 1 | | using System.Linq.Expressions; |
| | 2 | | using System.Reflection; |
| | 3 | | using System.Text.RegularExpressions; |
| | 4 | |
|
| | 5 | | namespace Towel.Mathematics; |
| | 6 | |
|
| | 7 | | /// <summary>Contains definitions necessary for the generic Symbolics class.</summary> |
| | 8 | | public static class Symbolics |
| | 9 | | { |
| | 10 | | #region OperatorPriority Enum |
| | 11 | |
|
| | 12 | | internal enum OperatorPriority |
| | 13 | | { |
| | 14 | | #pragma warning disable CA1069 // Enums values should not be duplicated |
| | 15 | | #pragma warning disable SA1602 // Enumeration items should be documented |
| | 16 | | Addition = 1, |
| | 17 | | Subtraction = 1, |
| | 18 | | Multiplication = 2, |
| | 19 | | Division = 2, |
| | 20 | | Exponents = 3, |
| | 21 | | Roots = 3, |
| | 22 | | Logical = 4, |
| | 23 | | Negation = 5, |
| | 24 | | Factorial = 6, |
| | 25 | | #pragma warning restore SA1602 // Enumeration items should be documented |
| | 26 | | #pragma warning restore CA1069 // Enums values should not be duplicated |
| | 27 | | } |
| | 28 | |
|
| | 29 | | #endregion |
| | 30 | |
|
| | 31 | | #region Attributes |
| | 32 | |
|
| | 33 | | [AttributeUsage(AttributeTargets.Class)] |
| | 34 | | internal abstract class RepresentationAttribute : Attribute |
| | 35 | | { |
| | 36 | | internal string[] _representations; |
| | 37 | |
|
| 2 | 38 | | internal RepresentationAttribute(string a, params string[] b) |
| 2 | 39 | | { |
| 2 | 40 | | if (string.IsNullOrWhiteSpace(a)) |
| 0 | 41 | | { |
| 0 | 42 | | throw new ArgumentException( |
| 0 | 43 | | "There is a BUG in " + nameof(Towel) + ". A " + |
| 0 | 44 | | nameof(Symbolics) + "." + nameof(RepresentationAttribute) + " representation is invalid."); |
| | 45 | | } |
| 6 | 46 | | foreach (string @string in b) |
| 0 | 47 | | { |
| 0 | 48 | | if (string.IsNullOrWhiteSpace(@string)) |
| 0 | 49 | | { |
| 0 | 50 | | throw new ArgumentException( |
| 0 | 51 | | "There is a BUG in " + nameof(Towel) + ". A " + |
| 0 | 52 | | nameof(Symbolics) + "." + nameof(RepresentationAttribute) + " representation is invalid."); |
| | 53 | | } |
| 0 | 54 | | } |
| 2 | 55 | | _representations = new string[b.Length + 1]; |
| 2 | 56 | | _representations[0] = a; |
| 6 | 57 | | for (int i = 1, j = 0; j < b.Length; i++, j++) |
| 0 | 58 | | { |
| 0 | 59 | | _representations[i] = b[j]; |
| 0 | 60 | | } |
| 2 | 61 | | } |
| | 62 | |
|
| | 63 | | internal string[] Representations |
| | 64 | | { |
| | 65 | | get |
| 2 | 66 | | { |
| 2 | 67 | | return _representations; |
| 2 | 68 | | } |
| | 69 | | } |
| | 70 | | } |
| | 71 | |
|
| | 72 | | [AttributeUsage(AttributeTargets.Class)] |
| | 73 | | internal class OperationAttribute : RepresentationAttribute |
| | 74 | | { |
| 3 | 75 | | internal OperationAttribute(string a, params string[] b) : base(a, b) { } |
| | 76 | | } |
| | 77 | |
|
| | 78 | | [AttributeUsage(AttributeTargets.Class)] |
| | 79 | | internal class LeftUnaryOperatorAttribute : Attribute |
| | 80 | | { |
| | 81 | | internal readonly string Representation; |
| | 82 | | internal readonly OperatorPriority Priority; |
| | 83 | |
|
| 1 | 84 | | internal LeftUnaryOperatorAttribute(string representation, OperatorPriority operatorPriority) : base() |
| 1 | 85 | | { |
| 1 | 86 | | Representation = representation; |
| 1 | 87 | | Priority = operatorPriority; |
| 1 | 88 | | } |
| | 89 | | } |
| | 90 | |
|
| | 91 | | [AttributeUsage(AttributeTargets.Class)] |
| | 92 | | internal class RightUnaryOperatorAttribute : Attribute |
| | 93 | | { |
| | 94 | | internal readonly string Representation; |
| | 95 | | internal readonly OperatorPriority Priority; |
| | 96 | |
|
| 1 | 97 | | internal RightUnaryOperatorAttribute(string representation, OperatorPriority operatorPriority) : base() |
| 1 | 98 | | { |
| 1 | 99 | | Representation = representation; |
| 1 | 100 | | Priority = operatorPriority; |
| 1 | 101 | | } |
| | 102 | | } |
| | 103 | |
|
| | 104 | | [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] |
| | 105 | | internal class BinaryOperatorAttribute : Attribute |
| | 106 | | { |
| | 107 | | internal readonly string Representation; |
| | 108 | | internal readonly OperatorPriority Priority; |
| | 109 | |
|
| 11 | 110 | | internal BinaryOperatorAttribute(string representation, OperatorPriority operatorPriority) : base() |
| 11 | 111 | | { |
| 11 | 112 | | Representation = representation; |
| 11 | 113 | | Priority = operatorPriority; |
| 11 | 114 | | } |
| | 115 | | } |
| | 116 | |
|
| | 117 | | [AttributeUsage(AttributeTargets.Class)] |
| | 118 | | internal class KnownConstantAttribute : RepresentationAttribute |
| | 119 | | { |
| 3 | 120 | | internal KnownConstantAttribute(string a, params string[] b) : base(a, b) { } |
| | 121 | | } |
| | 122 | |
|
| | 123 | | #endregion |
| | 124 | |
|
| | 125 | | #region Expression + Inheriters |
| | 126 | |
|
| | 127 | | #region Expression |
| | 128 | |
|
| | 129 | | #pragma warning disable CS0660 // Type defines operator == or operator != but does not override Object.Equals(object o) |
| | 130 | | #pragma warning disable CS0661 // Type defines operator == or operator != but does not override Object.GetHashCode() |
| | 131 | | /// <summary>Abstract base class for mathematical expressions.</summary> |
| | 132 | | public abstract class Expression |
| | 133 | | #pragma warning restore CS0661 // Type defines operator == or operator != but does not override Object.GetHashCode() |
| | 134 | | #pragma warning restore CS0660 // Type defines operator == or operator != but does not override Object.Equals(object o) |
| | 135 | | { |
| | 136 | | /// <summary>Simplifies the mathematical expression.</summary> |
| | 137 | | /// <returns>The simplified mathematical expression.</returns> |
| 140 | 138 | | public virtual Expression Simplify() => Clone(); |
| | 139 | |
|
| | 140 | | /// <summary>Substitutes an expression for all occurences of a variable.</summary> |
| | 141 | | /// <param name="variable">The variable to be substititued.</param> |
| | 142 | | /// <param name="expression">The expression to substitute for each occurence of a variable.</param> |
| | 143 | | /// <returns>The resulting expression of the substitution.</returns> |
| 0 | 144 | | public virtual Expression Substitute(string variable, Expression expression) => Clone(); |
| | 145 | |
|
| | 146 | | /// <summary>Substitutes an expression for all occurences of a variable.</summary> |
| | 147 | | /// <typeparam name="T">The type of value to substitute in for the variable.</typeparam> |
| | 148 | | /// <param name="variable">The variable to be substititued.</param> |
| | 149 | | /// <param name="value">The value to substitute for each occurence of a variable.</param> |
| | 150 | | /// <returns>The resulting expression of the substitution.</returns> |
| 0 | 151 | | public Expression Substitute<T>(string variable, T value) => SubstitutionHack(variable, new Constant<T>(value)); |
| | 152 | |
|
| 0 | 153 | | internal Expression SubstitutionHack(string variable, Expression expression) => Substitute(variable, expression); |
| | 154 | | /// <summary>Derives this expression.</summary> |
| | 155 | | /// <param name="variable">The variable to derive in relation to.</param> |
| | 156 | | /// <returns>The result of the derivation.</returns> |
| | 157 | | #warning TODO |
| 0 | 158 | | public virtual Expression Derive(string variable) => throw new NotImplementedException("This feature is still in dev |
| | 159 | | /// <summary>Integrates this expression.</summary> |
| | 160 | | /// <param name="variable">The variable to integrate in relation to.</param> |
| | 161 | | /// <returns>The result of the integration.</returns> |
| | 162 | | #warning TODO |
| 0 | 163 | | public virtual Expression Integrate(string variable) => throw new NotImplementedException("This feature is still in |
| | 164 | |
|
| | 165 | | /// <summary>Creates a copy of the expression.</summary> |
| | 166 | | /// <returns>A copy of the expression.</returns> |
| | 167 | | public abstract Expression Clone(); |
| | 168 | |
|
| | 169 | | /// <summary>Negates an expression.</summary> |
| | 170 | | /// <param name="a">The expression to negate.</param> |
| | 171 | | /// <returns>The result of the negation.</returns> |
| 4 | 172 | | public static Expression operator -(Expression a) => new Negate(a); |
| | 173 | | /// <summary>Adds two expressions.</summary> |
| | 174 | | /// <param name="a">The first expression of the addition.</param> |
| | 175 | | /// <param name="b">The second expression of the addition.</param> |
| | 176 | | /// <returns>The result of the addition.</returns> |
| 15 | 177 | | public static Expression operator +(Expression a, Expression b) => new Add(a, b); |
| | 178 | | /// <summary>Subtracts two expressions.</summary> |
| | 179 | | /// <param name="a">The first expression of the subtraction.</param> |
| | 180 | | /// <param name="b">The second expression of the subtraction.</param> |
| | 181 | | /// <returns>The result of the subtraction.</returns> |
| 13 | 182 | | public static Expression operator -(Expression a, Expression b) => new Subtract(a, b); |
| | 183 | | /// <summary>Multiplies two expressions.</summary> |
| | 184 | | /// <param name="a">The first expression of the multiplication.</param> |
| | 185 | | /// <param name="b">The second expression of the multiplication.</param> |
| | 186 | | /// <returns>The result of the multiplication.</returns> |
| 21 | 187 | | public static Expression operator *(Expression a, Expression b) => new Multiply(a, b); |
| | 188 | | /// <summary>Divides two expressions.</summary> |
| | 189 | | /// <param name="a">The first expression of the division.</param> |
| | 190 | | /// <param name="b">The second expression of the division.</param> |
| | 191 | | /// <returns>The result of the division.</returns> |
| 11 | 192 | | public static Expression operator /(Expression a, Expression b) => new Divide(a, b); |
| | 193 | | /// <summary>Wraps two expressions in an equality check.</summary> |
| | 194 | | /// <param name="a">The left side of the equality.</param> |
| | 195 | | /// <param name="b">The right side of the equality.</param> |
| | 196 | | /// <returns>The expressions wrapped in an equality check.</returns> |
| 0 | 197 | | public static Expression operator ==(Expression a, Expression b) => new Equal(a, b); |
| | 198 | | /// <summary>Wraps two expressions in an inequality check.</summary> |
| | 199 | | /// <param name="a">The left side of the inequality.</param> |
| | 200 | | /// <param name="b">The right side of the inequality.</param> |
| | 201 | | /// <returns>The expressions wrapped in an inequality check.</returns> |
| 0 | 202 | | public static Expression operator !=(Expression a, Expression b) => new NotEqual(a, b); |
| | 203 | | /// <summary>Wraps two expressions in a less than check.</summary> |
| | 204 | | /// <param name="a">The left side of the less than.</param> |
| | 205 | | /// <param name="b">The right side of the less than.</param> |
| | 206 | | /// <returns>The expressions wrapped in an less than check.</returns> |
| 0 | 207 | | public static Expression operator <(Expression a, Expression b) => new LessThan(a, b); |
| | 208 | | /// <summary>Wraps two expressions in a greater than check.</summary> |
| | 209 | | /// <param name="a">The left side of the greater than.</param> |
| | 210 | | /// <param name="b">The right side of the greater than.</param> |
| | 211 | | /// <returns>The expressions wrapped in an greater than check.</returns> |
| 0 | 212 | | public static Expression operator >(Expression a, Expression b) => new GreaterThan(a, b); |
| | 213 | | /// <summary>Takes one expression to the power of another.</summary> |
| | 214 | | /// <param name="a">The first expression of the power operation.</param> |
| | 215 | | /// <param name="b">The second expression of the power operation.</param> |
| | 216 | | /// <returns>The result of the power operation.</returns> |
| 0 | 217 | | public static Expression operator ^(Expression a, Expression b) => new Power(a, b); |
| | 218 | | } |
| | 219 | |
|
| | 220 | | #endregion |
| | 221 | |
|
| | 222 | | #region Variable |
| | 223 | |
|
| | 224 | | /// <summary>A variable in a symbolic mathematics expression.</summary> |
| | 225 | | public class Variable : Expression |
| | 226 | | { |
| | 227 | | /// <summary>The name of the variable.</summary> |
| 0 | 228 | | public string Name { get; } |
| | 229 | |
|
| | 230 | | /// <summary>Constructs a new variable.</summary> |
| | 231 | | /// <param name="name">The name of the vairable.</param> |
| 0 | 232 | | public Variable(string name) { Name = name; } |
| | 233 | |
|
| | 234 | | /// <summary>Clones this expression.</summary> |
| | 235 | | /// <returns>A clone of this expression.</returns> |
| 0 | 236 | | public override Expression Clone() => new Variable(Name); |
| | 237 | |
|
| | 238 | | /// <summary>Substitutes an expression for all occurences of a variable.</summary> |
| | 239 | | /// <param name="variable">The variable to be substititued.</param> |
| | 240 | | /// <param name="expression">The expression to substitute for each occurence of a variable.</param> |
| | 241 | | /// <returns>The resulting expression of the substitution.</returns> |
| | 242 | | public override Expression Substitute(string variable, Expression expression) |
| 0 | 243 | | { |
| 0 | 244 | | if (Name == variable) |
| 0 | 245 | | { |
| 0 | 246 | | return expression.Clone(); |
| | 247 | | } |
| | 248 | | else |
| 0 | 249 | | { |
| 0 | 250 | | return base.Substitute(variable, expression); |
| | 251 | | } |
| 0 | 252 | | } |
| | 253 | |
|
| | 254 | | /// <summary>Standard conversion to a string representation.</summary> |
| | 255 | | /// <returns>The string represnetation of this expression.</returns> |
| 0 | 256 | | public override string ToString() => "[" + Name + "]"; |
| | 257 | |
|
| | 258 | | /// <summary>Standard equality check.</summary> |
| | 259 | | /// <param name="b">The object to check for equality with.</param> |
| | 260 | | /// <returns>True if equal. False if not.</returns> |
| | 261 | | public override bool Equals(object? b) |
| 0 | 262 | | { |
| 0 | 263 | | if (b is Variable) |
| 0 | 264 | | { |
| 0 | 265 | | return Name.Equals(b as Variable); |
| | 266 | | } |
| 0 | 267 | | return false; |
| 0 | 268 | | } |
| | 269 | |
|
| | 270 | | /// <summary>Standard hash function.</summary> |
| | 271 | | /// <returns>The computed hash code for this instance.</returns> |
| 0 | 272 | | public override int GetHashCode() => Name.GetHashCode(); |
| | 273 | | } |
| | 274 | |
|
| | 275 | | #endregion |
| | 276 | |
|
| | 277 | | #region Constant + Inheriters |
| | 278 | |
|
| | 279 | | #region Constant |
| | 280 | |
|
| | 281 | | /// <summary>Represents a constant numerical value.</summary> |
| | 282 | | public abstract class Constant : Expression |
| | 283 | | { |
| | 284 | | /// <summary>True if this is a known constant value.</summary> |
| 34 | 285 | | public virtual bool IsKnownConstant => false; |
| | 286 | | /// <summary>True if this numeric value is zero (0).</summary> |
| 0 | 287 | | public virtual bool IsZero => false; |
| | 288 | | /// <summary>True if this numeric value is one (1).</summary> |
| 0 | 289 | | public virtual bool IsOne => false; |
| | 290 | | /// <summary>True if this numeric value is two (2).</summary> |
| 0 | 291 | | public virtual bool IsTwo => false; |
| | 292 | | /// <summary>True if this numeric value is three (3).</summary> |
| 0 | 293 | | public virtual bool IsThree => false; |
| | 294 | | /// <summary>True if this numeric value is π (pi).</summary> |
| 0 | 295 | | public virtual bool IsPi => false; |
| | 296 | |
|
| | 297 | | /// <summary>Determines if the constant is negative.</summary> |
| | 298 | | public abstract bool IsNegative { get; } |
| | 299 | |
|
| | 300 | | internal virtual Expression Simplify(Operation operation, params Expression[] operands) |
| 0 | 301 | | { |
| 0 | 302 | | return this; |
| 0 | 303 | | } |
| | 304 | |
|
| 0 | 305 | | internal static System.Collections.Generic.Dictionary<Type, Func<object, Expression>> preCompiledConstructors = |
| 0 | 306 | | new(); |
| | 307 | |
|
| | 308 | | internal static Expression BuildGeneric(object value) |
| 0 | 309 | | { |
| 0 | 310 | | Type valueType = value.GetType(); |
| 0 | 311 | | if (preCompiledConstructors.TryGetValue(valueType, out var preCompiledConstructor)) |
| 0 | 312 | | { |
| 0 | 313 | | return preCompiledConstructor(value); |
| | 314 | | } |
| | 315 | | else |
| 0 | 316 | | { |
| 0 | 317 | | Type constantType = typeof(Constant<>).MakeGenericType(valueType); |
| 0 | 318 | | ConstructorInfo? constructorInfo = constantType.GetConstructor(Ɐ(valueType)); |
| 0 | 319 | | if (constructorInfo is null) |
| 0 | 320 | | { |
| 0 | 321 | | throw new TowelBugException($"Encountered null {nameof(ConstructorInfo)} in {nameof(BuildGeneric)}."); |
| | 322 | | } |
| 0 | 323 | | ParameterExpression A = System.Linq.Expressions.Expression.Parameter(typeof(object)); |
| 0 | 324 | | NewExpression newExpression = System.Linq.Expressions.Expression.New(constructorInfo, System.Linq.Expressions.Ex |
| 0 | 325 | | Func<object, Expression> newFunction = System.Linq.Expressions.Expression.Lambda<Func<object, Expression>>(newEx |
| 0 | 326 | | preCompiledConstructors.Add(valueType, newFunction); |
| 0 | 327 | | return newFunction(value); |
| | 328 | | } |
| 0 | 329 | | } |
| | 330 | | } |
| | 331 | |
|
| | 332 | | #endregion |
| | 333 | |
|
| | 334 | | #region KnownConstantOfUnknownType + Inheriters |
| | 335 | |
|
| | 336 | | #region KnownConstantOfUknownType |
| | 337 | |
|
| | 338 | | /// <summary>Abstract base class for known constants of unknown types.</summary> |
| | 339 | | public abstract class KnownConstantOfUnknownType : Constant |
| | 340 | | { |
| | 341 | | /// <summary>True if this numeric value is a known value.</summary> |
| 0 | 342 | | public override bool IsKnownConstant => true; |
| | 343 | |
|
| | 344 | | internal abstract Constant<T> ApplyType<T>(); |
| | 345 | | } |
| | 346 | |
|
| | 347 | | #endregion |
| | 348 | |
|
| | 349 | | #region Pi |
| | 350 | |
|
| | 351 | | /// <summary>Represents the π (pi).</summary> |
| | 352 | | [KnownConstant("Ï€")] |
| | 353 | | public class Pi : KnownConstantOfUnknownType |
| | 354 | | { |
| | 355 | | /// <summary>Constructs a new instance of pi.</summary> |
| 54 | 356 | | public Pi() : base() { } |
| | 357 | |
|
| | 358 | | /// <summary>True if this numeric value is π (pi).</summary> |
| 0 | 359 | | public override bool IsPi => true; |
| | 360 | |
|
| | 361 | | /// <summary>Determines if the constant is negative.</summary> |
| 0 | 362 | | public override bool IsNegative => false; |
| | 363 | |
|
| 18 | 364 | | internal override Constant<T> ApplyType<T>() => new Pi<T>(); |
| | 365 | |
|
| | 366 | | /// <summary>Clones this expression.</summary> |
| | 367 | | /// <returns>A clone of this expression.</returns> |
| 0 | 368 | | public override Expression Clone() => new Pi(); |
| | 369 | |
|
| | 370 | | /// <summary>Standard conversion to a string representation.</summary> |
| | 371 | | /// <returns>The string represnetation of this expression.</returns> |
| 0 | 372 | | public override string ToString() => "Ï€"; |
| | 373 | |
|
| | 374 | | /// <summary>Standard equality check.</summary> |
| | 375 | | /// <param name="b">The object to check for equality with.</param> |
| | 376 | | /// <returns>True if equal. False if not.</returns> |
| | 377 | | public override bool Equals(object? b) |
| 0 | 378 | | { |
| 0 | 379 | | if (b is Pi) |
| 0 | 380 | | { |
| 0 | 381 | | return true; |
| | 382 | | } |
| 0 | 383 | | return false; |
| 0 | 384 | | } |
| | 385 | |
|
| | 386 | | /// <summary>The default hash code for this instance.</summary> |
| | 387 | | /// <returns>The computed hash code.</returns> |
| 0 | 388 | | public override int GetHashCode() => HashCode; |
| 0 | 389 | | internal static readonly int HashCode = nameof(Pi).GetHashCode(); |
| | 390 | | } |
| | 391 | |
|
| | 392 | | #endregion |
| | 393 | |
|
| | 394 | | #region Zero |
| | 395 | |
|
| | 396 | | /// <summary>Represents zero (0).</summary> |
| | 397 | | public class Zero : KnownConstantOfUnknownType |
| | 398 | | { |
| | 399 | | /// <summary>Constructs a new zero (0) value.</summary> |
| 0 | 400 | | public Zero() : base() { } |
| | 401 | |
|
| | 402 | | /// <summary>True if this numeric value is zero (0).</summary> |
| 0 | 403 | | public override bool IsZero => true; |
| | 404 | |
|
| | 405 | | /// <summary>Determines if the constant is negative.</summary> |
| 0 | 406 | | public override bool IsNegative => false; |
| | 407 | |
|
| 0 | 408 | | internal override Constant<T> ApplyType<T>() => new Zero<T>(); |
| | 409 | |
|
| | 410 | | /// <summary>Clones this expression.</summary> |
| | 411 | | /// <returns>A clone of this expression.</returns> |
| 0 | 412 | | public override Expression Clone() => new Zero(); |
| | 413 | |
|
| | 414 | | /// <summary>Standard conversion to a string representation.</summary> |
| | 415 | | /// <returns>The string represnetation of this expression.</returns> |
| 0 | 416 | | public override string ToString() => "0"; |
| | 417 | |
|
| | 418 | | /// <summary>Standard equality check.</summary> |
| | 419 | | /// <param name="b">The object to check for equality with.</param> |
| | 420 | | /// <returns>True if equal. False if not.</returns> |
| | 421 | | public override bool Equals(object? b) |
| 0 | 422 | | { |
| 0 | 423 | | if (b is Zero) |
| 0 | 424 | | { |
| 0 | 425 | | return true; |
| | 426 | | } |
| 0 | 427 | | return false; |
| 0 | 428 | | } |
| | 429 | |
|
| 0 | 430 | | internal static readonly int HashCode = nameof(Zero).GetHashCode(); |
| | 431 | |
|
| | 432 | | /// <summary>The default hash code computation.</summary> |
| | 433 | | /// <returns>The computed hash code for this instance.</returns> |
| 0 | 434 | | public override int GetHashCode() => HashCode; |
| | 435 | | } |
| | 436 | |
|
| | 437 | | #endregion |
| | 438 | |
|
| | 439 | | #region One |
| | 440 | |
|
| | 441 | | /// <summary>Represents the value of one (1).</summary> |
| | 442 | | public class One : KnownConstantOfUnknownType |
| | 443 | | { |
| | 444 | | /// <summary>Constructs a new one (1) constant.</summary> |
| 0 | 445 | | public One() : base() { } |
| | 446 | |
|
| | 447 | | /// <summary>True if this numeric value is one (1).</summary> |
| 0 | 448 | | public override bool IsOne => true; |
| | 449 | |
|
| | 450 | | /// <summary>Determines if the constant is negative.</summary> |
| 0 | 451 | | public override bool IsNegative => false; |
| | 452 | |
|
| 0 | 453 | | internal override Constant<T> ApplyType<T>() => new One<T>(); |
| | 454 | |
|
| | 455 | | /// <summary>Clones this expression.</summary> |
| | 456 | | /// <returns>A clone of this expression.</returns> |
| 0 | 457 | | public override Expression Clone() => new One(); |
| | 458 | |
|
| | 459 | | /// <summary>Standard conversion to a string representation.</summary> |
| | 460 | | /// <returns>The string represnetation of this expression.</returns> |
| 0 | 461 | | public override string ToString() => "1"; |
| | 462 | |
|
| | 463 | | /// <summary>Standard equality check.</summary> |
| | 464 | | /// <param name="b">The object to check for equality with.</param> |
| | 465 | | /// <returns>True if equal. False if not.</returns> |
| | 466 | | public override bool Equals(object? b) |
| 0 | 467 | | { |
| 0 | 468 | | if (b is One) |
| 0 | 469 | | { |
| 0 | 470 | | return true; |
| | 471 | | } |
| 0 | 472 | | return false; |
| 0 | 473 | | } |
| | 474 | |
|
| 0 | 475 | | internal static readonly int HashCode = nameof(One).GetHashCode(); |
| | 476 | |
|
| | 477 | | /// <summary>The default hash code computation.</summary> |
| | 478 | | /// <returns>The computed hash code for this instance.</returns> |
| 0 | 479 | | public override int GetHashCode() => HashCode; |
| | 480 | | } |
| | 481 | |
|
| | 482 | | #endregion |
| | 483 | |
|
| | 484 | | #region Two |
| | 485 | |
|
| | 486 | | /// <summary>Represents the value of two (2).</summary> |
| | 487 | | public class Two : KnownConstantOfUnknownType |
| | 488 | | { |
| | 489 | | /// <summary>Constructs a new value of two (2).</summary> |
| 0 | 490 | | public Two() : base() { } |
| | 491 | |
|
| | 492 | | /// <summary>True if this numeric value is two (2).</summary> |
| 0 | 493 | | public override bool IsTwo => true; |
| | 494 | |
|
| | 495 | | /// <summary>Determines if the constant is negative.</summary> |
| 0 | 496 | | public override bool IsNegative => false; |
| | 497 | |
|
| 0 | 498 | | internal override Constant<T> ApplyType<T>() => new Two<T>(); |
| | 499 | |
|
| | 500 | | /// <summary>Clones this expression.</summary> |
| | 501 | | /// <returns>A clone of this expression.</returns> |
| 0 | 502 | | public override Expression Clone() => new Two(); |
| | 503 | |
|
| | 504 | | /// <summary>Standard conversion to a string representation.</summary> |
| | 505 | | /// <returns>The string represnetation of this expression.</returns> |
| 0 | 506 | | public override string ToString() => "2"; |
| | 507 | |
|
| | 508 | | /// <summary>Standard equality check.</summary> |
| | 509 | | /// <param name="b">The object to check for equality with.</param> |
| | 510 | | /// <returns>True if equal. False if not.</returns> |
| | 511 | | public override bool Equals(object? b) |
| 0 | 512 | | { |
| 0 | 513 | | if (b is Two) |
| 0 | 514 | | { |
| 0 | 515 | | return true; |
| | 516 | | } |
| 0 | 517 | | return false; |
| 0 | 518 | | } |
| | 519 | |
|
| 0 | 520 | | internal static readonly int HashCode = nameof(Two).GetHashCode(); |
| | 521 | |
|
| | 522 | | /// <summary>The default hash code computation.</summary> |
| | 523 | | /// <returns>The computed hash code for this instance.</returns> |
| 0 | 524 | | public override int GetHashCode() => HashCode; |
| | 525 | | } |
| | 526 | |
|
| | 527 | | #endregion |
| | 528 | |
|
| | 529 | | #region Three |
| | 530 | |
|
| | 531 | | /// <summary>Represents the value of three (3).</summary> |
| | 532 | | public class Three : KnownConstantOfUnknownType |
| | 533 | | { |
| | 534 | | /// <summary>Constructs a new value of three (3).</summary> |
| 0 | 535 | | public Three() : base() { } |
| | 536 | |
|
| | 537 | | /// <summary>True if this numeric value is three (3).</summary> |
| 0 | 538 | | public override bool IsThree => true; |
| | 539 | |
|
| | 540 | | /// <summary>Determines if the constant is negative.</summary> |
| 0 | 541 | | public override bool IsNegative => false; |
| | 542 | |
|
| 0 | 543 | | internal override Constant<T> ApplyType<T>() => new Three<T>(); |
| | 544 | |
|
| | 545 | | /// <summary>Clones this expression.</summary> |
| | 546 | | /// <returns>A clone of this expression.</returns> |
| 0 | 547 | | public override Expression Clone() => new Three(); |
| | 548 | |
|
| | 549 | | /// <summary>Standard conversion to a string representation.</summary> |
| | 550 | | /// <returns>The string represnetation of this expression.</returns> |
| 0 | 551 | | public override string ToString() => "3"; |
| | 552 | |
|
| | 553 | | /// <summary>Standard equality check.</summary> |
| | 554 | | /// <param name="b">The object to check for equality with.</param> |
| | 555 | | /// <returns>True if equal. False if not.</returns> |
| | 556 | | public override bool Equals(object? b) |
| 0 | 557 | | { |
| 0 | 558 | | if (b is Three) |
| 0 | 559 | | { |
| 0 | 560 | | return true; |
| | 561 | | } |
| 0 | 562 | | return false; |
| 0 | 563 | | } |
| | 564 | |
|
| 0 | 565 | | internal static readonly int HashCode = nameof(Three).GetHashCode(); |
| | 566 | |
|
| | 567 | | /// <summary>The default hash code computation.</summary> |
| | 568 | | /// <returns>The computed hash code for this instance.</returns> |
| 0 | 569 | | public override int GetHashCode() => HashCode; |
| | 570 | | } |
| | 571 | |
|
| | 572 | | #endregion |
| | 573 | |
|
| | 574 | | #endregion |
| | 575 | |
|
| | 576 | | #region Constant<T> + Inheriters |
| | 577 | |
|
| | 578 | | #region Constant<T> |
| | 579 | |
|
| | 580 | | /// <summary>Represents a numeric constant.</summary> |
| | 581 | | /// <typeparam name="T">The generic type of the numeric value.</typeparam> |
| | 582 | | public class Constant<T> : Constant |
| | 583 | | { |
| | 584 | | /// <summary>The value of this numeric constant.</summary> |
| | 585 | | public readonly T Value; |
| | 586 | |
|
| | 587 | | /// <summary>True if this numeric value is zero (0).</summary> |
| 18 | 588 | | public override bool IsZero => Equate(Value, Towel.Constant<T>.Zero); |
| | 589 | | /// <summary>True if this numeric value is one (1).</summary> |
| 0 | 590 | | public override bool IsOne => Equate(Value, Towel.Constant<T>.One); |
| | 591 | | /// <summary>True if this numeric value is two (2).</summary> |
| 0 | 592 | | public override bool IsTwo => Equate(Value, Towel.Constant<T>.Two); |
| | 593 | | /// <summary>True if this numeric value is three (3).</summary> |
| 0 | 594 | | public override bool IsThree => Equate(Value, Towel.Constant<T>.Three); |
| | 595 | |
|
| | 596 | | /// <summary>Determines if the constant is negative.</summary> |
| 18 | 597 | | public override bool IsNegative => IsNegative(Value); |
| | 598 | |
|
| | 599 | | /// <summary>Constructs a new numeric constant.</summary> |
| | 600 | | /// <param name="constant">The value of the numeric constant.</param> |
| 1588 | 601 | | public Constant(T constant) { Value = constant; } |
| | 602 | |
|
| | 603 | | internal override Expression Simplify(Operation operation, params Expression[] operands) |
| 24 | 604 | | { |
| 24 | 605 | | return operation.SimplifyHack<T>(operands); |
| 24 | 606 | | } |
| | 607 | |
|
| | 608 | | /// <summary>Clones this expression.</summary> |
| | 609 | | /// <returns>A clone of this expression.</returns> |
| 122 | 610 | | public override Expression Clone() => new Constant<T>(Value); |
| | 611 | |
|
| | 612 | | /// <summary>Standard conversion to a string representation.</summary> |
| | 613 | | /// <returns>The string represnetation of this expression.</returns> |
| 176 | 614 | | public override string? ToString() => Value?.ToString(); |
| | 615 | |
|
| | 616 | | /// <summary>Standard equality check.</summary> |
| | 617 | | /// <param name="b">The object to check for equality with.</param> |
| | 618 | | /// <returns>True if equal. False if not.</returns> |
| | 619 | | public override bool Equals(object? b) |
| 92 | 620 | | { |
| 92 | 621 | | if (b is Constant<T> B) |
| 92 | 622 | | { |
| 92 | 623 | | return Equate(Value, B.Value); |
| | 624 | | } |
| 0 | 625 | | return false; |
| 92 | 626 | | } |
| | 627 | |
|
| 0 | 628 | | internal static readonly int HashCode = nameof(Constant<T>).GetHashCode(); |
| | 629 | |
|
| | 630 | | /// <summary>The default hash code computation.</summary> |
| | 631 | | /// <returns>The computed hash code for this instance.</returns> |
| 0 | 632 | | public override int GetHashCode() => HashCode; |
| | 633 | | } |
| | 634 | |
|
| | 635 | | #endregion |
| | 636 | |
|
| | 637 | | #region KnownConstantOfKnownType<T> + Inheriters |
| | 638 | |
|
| | 639 | | #region KnownConstantOfKnownType<T> |
| | 640 | |
|
| | 641 | | /// <summary>Abstract base class for known constants of unknown types.</summary> |
| | 642 | | /// <typeparam name="T">The type of the known constant value.</typeparam> |
| | 643 | | public abstract class KnownConstantOfKnownType<T> : Constant<T> |
| | 644 | | { |
| | 645 | | /// <summary>True if this numeric value is a known value.</summary> |
| 0 | 646 | | public override bool IsKnownConstant => true; |
| | 647 | |
|
| | 648 | | /// <summary>Constructs a new constant of a known type.</summary> |
| | 649 | | /// <param name="constant">The value of the known constant.</param> |
| 108 | 650 | | public KnownConstantOfKnownType(T constant) : base(constant) { } |
| | 651 | | } |
| | 652 | |
|
| | 653 | | #endregion |
| | 654 | |
|
| | 655 | | #region Pi<T> |
| | 656 | |
|
| | 657 | | /// <summary>Represents the value of π (pi).</summary> |
| | 658 | | /// <typeparam name="T">The generic type of the numeric.</typeparam> |
| | 659 | | public class Pi<T> : KnownConstantOfKnownType<T> |
| | 660 | | { |
| | 661 | | /// <summary>Constructs a new value of π (pi).</summary> |
| 108 | 662 | | public Pi() : base(Towel.Constant<T>.Pi) { } |
| | 663 | |
|
| | 664 | | /// <summary>True if the value is π (pi).</summary> |
| 0 | 665 | | public override bool IsPi => true; |
| | 666 | |
|
| | 667 | | /// <summary>Clones this expression.</summary> |
| | 668 | | /// <returns>A clone of this expression.</returns> |
| 18 | 669 | | public override Expression Clone() => new Pi<T>(); |
| | 670 | |
|
| | 671 | | /// <summary>Standard conversion to a string representation.</summary> |
| | 672 | | /// <returns>The string represnetation of this expression.</returns> |
| 0 | 673 | | public override string ToString() => "Ï€"; |
| | 674 | |
|
| | 675 | | /// <summary>Standard equality check.</summary> |
| | 676 | | /// <param name="b">The object to check for equality with.</param> |
| | 677 | | /// <returns>True if equal. False if not.</returns> |
| | 678 | | public override bool Equals(object? b) |
| 0 | 679 | | { |
| 0 | 680 | | if (b is Pi<T>) |
| 0 | 681 | | { |
| 0 | 682 | | return true; |
| | 683 | | } |
| 0 | 684 | | return false; |
| 0 | 685 | | } |
| | 686 | |
|
| 0 | 687 | | internal static new readonly int HashCode = nameof(Pi<T>).GetHashCode(); |
| | 688 | |
|
| | 689 | | /// <summary>The default hash code computation.</summary> |
| | 690 | | /// <returns>The computed hash code for this instance.</returns> |
| 0 | 691 | | public override int GetHashCode() => HashCode; |
| | 692 | | } |
| | 693 | |
|
| | 694 | | #endregion |
| | 695 | |
|
| | 696 | | #region Zero<T> |
| | 697 | |
|
| | 698 | | /// <summary>Represents the value of zero (0).</summary> |
| | 699 | | /// <typeparam name="T">The generic type of the numeric value.</typeparam> |
| | 700 | | public class Zero<T> : KnownConstantOfKnownType<T> |
| | 701 | | { |
| | 702 | | /// <summary>Constructs a new zero (0) value.</summary> |
| 0 | 703 | | public Zero() : base(Towel.Constant<T>.Zero) { } |
| | 704 | |
|
| | 705 | | /// <summary>True if the value is zero (0).</summary> |
| 0 | 706 | | public override bool IsZero => true; |
| | 707 | |
|
| | 708 | | /// <summary>Clones this expression.</summary> |
| | 709 | | /// <returns>A clone of this expression.</returns> |
| 0 | 710 | | public override Expression Clone() => new Zero<T>(); |
| | 711 | |
|
| | 712 | | /// <summary>Standard conversion to a string representation.</summary> |
| | 713 | | /// <returns>The string represnetation of this expression.</returns> |
| 0 | 714 | | public override string ToString() => "0"; |
| | 715 | |
|
| | 716 | | /// <summary>Standard equality check.</summary> |
| | 717 | | /// <param name="b">The object to check for equality with.</param> |
| | 718 | | /// <returns>True if equal. False if not.</returns> |
| | 719 | | public override bool Equals(object? b) |
| 0 | 720 | | { |
| 0 | 721 | | if (b is Zero<T>) |
| 0 | 722 | | { |
| 0 | 723 | | return true; |
| | 724 | | } |
| 0 | 725 | | return false; |
| 0 | 726 | | } |
| | 727 | |
|
| 0 | 728 | | internal static new readonly int HashCode = nameof(Zero<T>).GetHashCode(); |
| | 729 | |
|
| | 730 | | /// <summary>The default hash code computation.</summary> |
| | 731 | | /// <returns>The computed hash code for this instance.</returns> |
| 0 | 732 | | public override int GetHashCode() => HashCode; |
| | 733 | | } |
| | 734 | |
|
| | 735 | | #endregion |
| | 736 | |
|
| | 737 | | #region One<T> |
| | 738 | |
|
| | 739 | | /// <summary>Represents the value of one (1).</summary> |
| | 740 | | /// <typeparam name="T">The generic type of the numeric value.</typeparam> |
| | 741 | | public class One<T> : KnownConstantOfKnownType<T> |
| | 742 | | { |
| | 743 | | /// <summary>Constructs a new one (1) value.</summary> |
| 0 | 744 | | public One() : base(Towel.Constant<T>.One) { } |
| | 745 | |
|
| | 746 | | /// <summary>True if the value is one (1).</summary> |
| 0 | 747 | | public override bool IsOne => true; |
| | 748 | |
|
| | 749 | | /// <summary>Clones this expression.</summary> |
| | 750 | | /// <returns>A clone of this expression.</returns> |
| 0 | 751 | | public override Expression Clone() => new One<T>(); |
| | 752 | |
|
| | 753 | | /// <summary>Standard conversion to a string representation.</summary> |
| | 754 | | /// <returns>The string represnetation of this expression.</returns> |
| 0 | 755 | | public override string ToString() => "1"; |
| | 756 | |
|
| | 757 | | /// <summary>Standard equality check.</summary> |
| | 758 | | /// <param name="b">The object to check for equality with.</param> |
| | 759 | | /// <returns>True if equal. False if not.</returns> |
| | 760 | | public override bool Equals(object? b) |
| 0 | 761 | | { |
| 0 | 762 | | if (b is One<T>) |
| 0 | 763 | | { |
| 0 | 764 | | return true; |
| | 765 | | } |
| 0 | 766 | | return false; |
| 0 | 767 | | } |
| | 768 | |
|
| 0 | 769 | | internal static new readonly int HashCode = nameof(One<T>).GetHashCode(); |
| | 770 | |
|
| | 771 | | /// <summary>The default hash code computation.</summary> |
| | 772 | | /// <returns>The computed hash code for this instance.</returns> |
| 0 | 773 | | public override int GetHashCode() => HashCode; |
| | 774 | | } |
| | 775 | |
|
| | 776 | | #endregion |
| | 777 | |
|
| | 778 | | #region Two<T> |
| | 779 | |
|
| | 780 | | /// <summary>Represents the value of two (2).</summary> |
| | 781 | | /// <typeparam name="T">The generic type of the numeric value.</typeparam> |
| | 782 | | public class Two<T> : KnownConstantOfKnownType<T> |
| | 783 | | { |
| | 784 | | /// <summary>Constructs a new value of two (2).</summary> |
| 0 | 785 | | public Two() : base(Towel.Constant<T>.Two) { } |
| | 786 | |
|
| | 787 | | /// <summary>True if the value is two (2).</summary> |
| 0 | 788 | | public override bool IsTwo => true; |
| | 789 | |
|
| | 790 | | /// <summary>Clones this expression.</summary> |
| | 791 | | /// <returns>A clone of this expression.</returns> |
| 0 | 792 | | public override Expression Clone() => new Two<T>(); |
| | 793 | |
|
| | 794 | | /// <summary>Standard conversion to a string representation.</summary> |
| | 795 | | /// <returns>The string represnetation of this expression.</returns> |
| 0 | 796 | | public override string ToString() => "2"; |
| | 797 | |
|
| | 798 | | /// <summary>Standard equality check.</summary> |
| | 799 | | /// <param name="b">The object to check for equality with.</param> |
| | 800 | | /// <returns>True if equal. False if not.</returns> |
| | 801 | | public override bool Equals(object? b) |
| 0 | 802 | | { |
| 0 | 803 | | if (b is Two<T>) |
| 0 | 804 | | { |
| 0 | 805 | | return true; |
| | 806 | | } |
| 0 | 807 | | return false; |
| 0 | 808 | | } |
| | 809 | |
|
| 0 | 810 | | internal static new readonly int HashCode = nameof(Two<T>).GetHashCode(); |
| | 811 | |
|
| | 812 | | /// <summary>The default hash code computation.</summary> |
| | 813 | | /// <returns>The computed hash code for this instance.</returns> |
| 0 | 814 | | public override int GetHashCode() => HashCode; |
| | 815 | | } |
| | 816 | |
|
| | 817 | | #endregion |
| | 818 | |
|
| | 819 | | #region Three<T> |
| | 820 | |
|
| | 821 | | /// <summary>Represents the value of three (3).</summary> |
| | 822 | | /// <typeparam name="T">The generic type of the numeric value.</typeparam> |
| | 823 | | public class Three<T> : KnownConstantOfKnownType<T> |
| | 824 | | { |
| | 825 | | /// <summary>Constructs a new value of three.</summary> |
| 0 | 826 | | public Three() : base(Towel.Constant<T>.Three) { } |
| | 827 | |
|
| | 828 | | /// <summary>True if the value is three (3).</summary> |
| 0 | 829 | | public override bool IsThree => true; |
| | 830 | |
|
| | 831 | | /// <summary>Clones this expression.</summary> |
| | 832 | | /// <returns>A clone of this expression.</returns> |
| 0 | 833 | | public override Expression Clone() => new Three<T>(); |
| | 834 | |
|
| | 835 | | /// <summary>Standard conversion to a string representation.</summary> |
| | 836 | | /// <returns>The string represnetation of this expression.</returns> |
| 0 | 837 | | public override string ToString() => "3"; |
| | 838 | |
|
| | 839 | | /// <summary>Standard equality check.</summary> |
| | 840 | | /// <param name="b">The object to check for equality with.</param> |
| | 841 | | /// <returns>True if equal. False if not.</returns> |
| | 842 | | public override bool Equals(object? b) |
| 0 | 843 | | { |
| 0 | 844 | | if (b is Three<T>) |
| 0 | 845 | | { |
| 0 | 846 | | return true; |
| | 847 | | } |
| 0 | 848 | | return false; |
| 0 | 849 | | } |
| | 850 | |
|
| 0 | 851 | | internal static new readonly int HashCode = nameof(Three<T>).GetHashCode(); |
| | 852 | |
|
| | 853 | | /// <summary>The default hash code computation.</summary> |
| | 854 | | /// <returns>The computed hash code for this instance.</returns> |
| 0 | 855 | | public override int GetHashCode() => HashCode; |
| | 856 | | } |
| | 857 | |
|
| | 858 | | #endregion |
| | 859 | |
|
| | 860 | | #region True |
| | 861 | |
|
| | 862 | | /// <summary>Represents the value of true.</summary> |
| | 863 | | public class True : KnownConstantOfKnownType<bool> |
| | 864 | | { |
| | 865 | | /// <summary>Constructs a new value of true.</summary> |
| 0 | 866 | | public True() : base(true) { } |
| | 867 | |
|
| | 868 | | /// <summary>Clones this expression.</summary> |
| | 869 | | /// <returns>A clone of this expression.</returns> |
| 0 | 870 | | public override Expression Clone() => new True(); |
| | 871 | |
|
| | 872 | | /// <summary>Standard conversion to a string representation.</summary> |
| | 873 | | /// <returns>The string represnetation of this expression.</returns> |
| 0 | 874 | | public override string ToString() => true.ToString(); |
| | 875 | |
|
| | 876 | | /// <summary>Standard equality check.</summary> |
| | 877 | | /// <param name="b">The object to check for equality with.</param> |
| | 878 | | /// <returns>True if equal. False if not.</returns> |
| | 879 | | public override bool Equals(object? b) |
| 0 | 880 | | { |
| 0 | 881 | | if (b is True) |
| 0 | 882 | | { |
| 0 | 883 | | return true; |
| | 884 | | } |
| 0 | 885 | | return false; |
| 0 | 886 | | } |
| | 887 | |
|
| 0 | 888 | | internal static new readonly int HashCode = nameof(True).GetHashCode(); |
| | 889 | |
|
| | 890 | | /// <summary>The default hash code computation.</summary> |
| | 891 | | /// <returns>The computed hash code for this instance.</returns> |
| 0 | 892 | | public override int GetHashCode() => HashCode; |
| | 893 | | } |
| | 894 | |
|
| | 895 | | #endregion |
| | 896 | |
|
| | 897 | | #region False |
| | 898 | |
|
| | 899 | | /// <summary>Represents the value of false.</summary> |
| | 900 | | public class False : KnownConstantOfKnownType<bool> |
| | 901 | | { |
| | 902 | | /// <summary>Constructs a new false value.</summary> |
| 0 | 903 | | public False() : base(true) { } |
| | 904 | |
|
| | 905 | | /// <summary>Clones this expression.</summary> |
| | 906 | | /// <returns>A clone of this expression.</returns> |
| 0 | 907 | | public override Expression Clone() => new False(); |
| | 908 | |
|
| | 909 | | /// <summary>Standard conversion to a string representation.</summary> |
| | 910 | | /// <returns>The string represnetation of this expression.</returns> |
| 0 | 911 | | public override string ToString() => false.ToString(); |
| | 912 | |
|
| | 913 | | /// <summary>Standard equality check.</summary> |
| | 914 | | /// <param name="b">The object to check for equality with.</param> |
| | 915 | | /// <returns>True if equal. False if not.</returns> |
| | 916 | | public override bool Equals(object? b) |
| 0 | 917 | | { |
| 0 | 918 | | if (b is False) |
| 0 | 919 | | { |
| 0 | 920 | | return true; |
| | 921 | | } |
| 0 | 922 | | return false; |
| 0 | 923 | | } |
| | 924 | |
|
| 0 | 925 | | internal static new readonly int HashCode = nameof(False).GetHashCode(); |
| | 926 | |
|
| | 927 | | /// <summary>The default hash code computation.</summary> |
| | 928 | | /// <returns>The computed hash code for this instance.</returns> |
| 0 | 929 | | public override int GetHashCode() => HashCode; |
| | 930 | | } |
| | 931 | |
|
| | 932 | | #endregion |
| | 933 | |
|
| | 934 | | #endregion |
| | 935 | |
|
| | 936 | | #endregion |
| | 937 | |
|
| | 938 | | #endregion |
| | 939 | |
|
| | 940 | | #region Operation + Inheriters |
| | 941 | |
|
| | 942 | | #region Operation |
| | 943 | |
|
| | 944 | | /// <summary>Abstract base class for all symbolic mathematics operations.</summary> |
| | 945 | | public abstract class Operation : Expression |
| | 946 | | { |
| | 947 | | /// <summary>Interface for symbolic mathematics operations that involve numeric computation.</summary> |
| | 948 | | public interface IMathematical { } |
| | 949 | | /// <summary>Interface for symbolic mathematics operations that involve logical computation.</summary> |
| | 950 | | public interface ILogical { } |
| | 951 | |
|
| | 952 | | internal virtual Expression Simplify<T>(params Expression[] operands) |
| 0 | 953 | | { |
| 0 | 954 | | return this; |
| 0 | 955 | | } |
| | 956 | |
|
| | 957 | | internal Expression SimplifyHack<T>(params Expression[] operands) |
| 24 | 958 | | { |
| 24 | 959 | | return Simplify<T>(operands); |
| 24 | 960 | | } |
| | 961 | | } |
| | 962 | |
|
| | 963 | | #endregion |
| | 964 | |
|
| | 965 | | #region Unary + Inheriters |
| | 966 | |
|
| | 967 | | #region Unary |
| | 968 | |
|
| | 969 | | #pragma warning disable CS0659 // Type overrides Object.Equals(object o) but does not override Object.GetHashCode() |
| | 970 | | /// <summary>Abstract base class for all symbolic mathematics unary operations.</summary> |
| | 971 | | public abstract class Unary : Operation |
| | 972 | | #pragma warning restore CS0659 // Type overrides Object.Equals(object o) but does not override Object.GetHashCode() |
| | 973 | | { |
| | 974 | | /// <summary>The operand of th unary operation.</summary> |
| 92 | 975 | | public Expression A { get; set; } |
| | 976 | |
|
| | 977 | | /// <summary>Constructs a new unary operation.</summary> |
| | 978 | | /// <param name="a">The operand of the unary operation.</param> |
| 112 | 979 | | public Unary(Expression a) : base() { A = a; } |
| | 980 | |
|
| | 981 | | /// <summary>Standard equality check.</summary> |
| | 982 | | /// <param name="b">The object to check for equality with.</param> |
| | 983 | | /// <returns>True if equal. False if not.</returns> |
| | 984 | | public override bool Equals(object? b) |
| 14 | 985 | | { |
| 14 | 986 | | if (b is not null && GetType() == b.GetType()) |
| 14 | 987 | | { |
| 14 | 988 | | return A.Equals(((Unary)b).A); |
| | 989 | | } |
| 0 | 990 | | return false; |
| 14 | 991 | | } |
| | 992 | | } |
| | 993 | |
|
| | 994 | | #endregion |
| | 995 | |
|
| | 996 | | #region Simplification |
| | 997 | |
|
| | 998 | | /// <summary>Represents a mathematical simplification operation.</summary> |
| | 999 | | [Operation("Simplify")] |
| | 1000 | | public class Simplification : Unary |
| | 1001 | | { |
| | 1002 | | /// <summary>Constructs a new simplification operation.</summary> |
| | 1003 | | /// <param name="a">The expression to simplify.</param> |
| 0 | 1004 | | public Simplification(Expression a) : base(a) { } |
| | 1005 | |
|
| | 1006 | | /// <summary>Simplifies the mathematical expression.</summary> |
| | 1007 | | /// <returns>The simplified mathematical expression.</returns> |
| 0 | 1008 | | public override Expression Simplify() => A.Simplify(); |
| | 1009 | |
|
| | 1010 | | /// <summary>Clones this expression.</summary> |
| | 1011 | | /// <returns>A clone of this expression.</returns> |
| 0 | 1012 | | public override Expression Clone() => new Simplification(A.Clone()); |
| | 1013 | |
|
| | 1014 | | /// <summary>Substitutes an expression for all occurences of a variable.</summary> |
| | 1015 | | /// <param name="variable">The variable to be substititued.</param> |
| | 1016 | | /// <param name="expression">The expression to substitute for each occurence of a variable.</param> |
| | 1017 | | /// <returns>The resulting expression of the substitution.</returns> |
| | 1018 | | public override Expression Substitute(string variable, Expression expression) => |
| 0 | 1019 | | new Simplification(A.Substitute(variable, expression)); |
| | 1020 | |
|
| | 1021 | | /// <summary>Standard conversion to a string representation.</summary> |
| | 1022 | | /// <returns>The string represnetation of this expression.</returns> |
| 0 | 1023 | | public override string ToString() => "Simplify(" + A + ")"; |
| | 1024 | | } |
| | 1025 | |
|
| | 1026 | | #endregion |
| | 1027 | |
|
| | 1028 | | #region Negate |
| | 1029 | |
|
| | 1030 | | /// <summary>Represents a negation operation.</summary> |
| | 1031 | | [LeftUnaryOperator("-", OperatorPriority.Negation)] |
| | 1032 | | public class Negate : Unary, Operation.IMathematical |
| | 1033 | | { |
| | 1034 | | /// <summary>Constructs a new negation operation.</summary> |
| | 1035 | | /// <param name="a">The expression to negate.</param> |
| 24 | 1036 | | public Negate(Expression a) : base(a) { } |
| | 1037 | |
|
| | 1038 | | /// <summary>Simplifies the mathematical expression.</summary> |
| | 1039 | | /// <returns>The simplified mathematical expression.</returns> |
| | 1040 | | public override Expression Simplify() |
| 0 | 1041 | | { |
| 0 | 1042 | | Expression OPERAND = A.Simplify(); |
| | 1043 | | #region Computation |
| | 1044 | | // Rule: [-A] => [B] where A is constant and B is -A |
| 0 | 1045 | | if (OPERAND is Constant constant) |
| 0 | 1046 | | { |
| 0 | 1047 | | return constant.Simplify(this, OPERAND); |
| | 1048 | | } |
| | 1049 | | #endregion |
| 0 | 1050 | | return -OPERAND; |
| 0 | 1051 | | } |
| | 1052 | |
|
| | 1053 | | internal override Expression Simplify<T>(params Expression[] operands) |
| 0 | 1054 | | { |
| 0 | 1055 | | if (operands[0] is Constant<T> a) |
| 0 | 1056 | | { |
| 0 | 1057 | | return new Constant<T>(Negation(a.Value)); |
| | 1058 | | } |
| 0 | 1059 | | return base.Simplify<T>(); |
| 0 | 1060 | | } |
| | 1061 | |
|
| | 1062 | | /// <summary>Clones this expression.</summary> |
| | 1063 | | /// <returns>A clone of this expression.</returns> |
| 0 | 1064 | | public override Expression Clone() => new Negate(A.Clone()); |
| | 1065 | |
|
| | 1066 | | /// <summary>Substitutes an expression for all occurences of a variable.</summary> |
| | 1067 | | /// <param name="variable">The variable to be substititued.</param> |
| | 1068 | | /// <param name="expression">The expression to substitute for each occurence of a variable.</param> |
| | 1069 | | /// <returns>The resulting expression of the substitution.</returns> |
| | 1070 | | public override Expression Substitute(string variable, Expression expression) => |
| 0 | 1071 | | new Negate(A.Substitute(variable, expression)); |
| | 1072 | |
|
| | 1073 | | /// <summary>Standard conversion to a string representation.</summary> |
| | 1074 | | /// <returns>The string represnetation of this expression.</returns> |
| | 1075 | | public override string ToString() |
| 8 | 1076 | | { |
| 8 | 1077 | | if (A is not Constant && A is not Variable) |
| 0 | 1078 | | { |
| 0 | 1079 | | return "-(" + A + ")"; |
| | 1080 | | } |
| 8 | 1081 | | return "-" + A; |
| 8 | 1082 | | } |
| | 1083 | | } |
| | 1084 | |
|
| | 1085 | | #endregion |
| | 1086 | |
|
| | 1087 | | #region NaturalLog |
| | 1088 | |
|
| | 1089 | | /// <summary>Represents a natural log operation.</summary> |
| | 1090 | | public class NaturalLog : Unary, Operation.IMathematical |
| | 1091 | | { |
| | 1092 | | /// <summary>Constructs a new natural log operation.</summary> |
| | 1093 | | /// <param name="a">The expression to compute the natrual log of.</param> |
| 0 | 1094 | | public NaturalLog(Expression a) : base(a) { } |
| | 1095 | |
|
| | 1096 | | /// <summary>Simplifies the mathematical expression.</summary> |
| | 1097 | | /// <returns>The simplified mathematical expression.</returns> |
| | 1098 | | public override Expression Simplify() |
| 0 | 1099 | | { |
| 0 | 1100 | | Expression OPERAND = A.Simplify(); |
| | 1101 | | #region Computation |
| | 1102 | | // Rule: [A] => [B] where A is constant and B is ln(A) |
| 0 | 1103 | | if (OPERAND is Constant constant) |
| 0 | 1104 | | { |
| 0 | 1105 | | return constant.Simplify(this, OPERAND); |
| | 1106 | | } |
| | 1107 | | #endregion |
| 0 | 1108 | | return new NaturalLog(OPERAND); |
| 0 | 1109 | | } |
| | 1110 | |
|
| | 1111 | | internal override Expression Simplify<T>(params Expression[] operands) |
| 0 | 1112 | | { |
| 0 | 1113 | | if (operands[0] is Constant<T> a) |
| 0 | 1114 | | { |
| 0 | 1115 | | return new Constant<T>(NaturalLogarithm(a.Value)); |
| | 1116 | | } |
| 0 | 1117 | | return base.Simplify<T>(); |
| 0 | 1118 | | } |
| | 1119 | |
|
| | 1120 | | /// <summary>Clones this expression.</summary> |
| | 1121 | | /// <returns>A clone of this expression.</returns> |
| 0 | 1122 | | public override Expression Clone() => new NaturalLog(A.Clone()); |
| | 1123 | |
|
| | 1124 | | /// <summary>Substitutes an expression for all occurences of a variable.</summary> |
| | 1125 | | /// <param name="variable">The variable to be substititued.</param> |
| | 1126 | | /// <param name="expression">The expression to substitute for each occurence of a variable.</param> |
| | 1127 | | /// <returns>The resulting expression of the substitution.</returns> |
| | 1128 | | public override Expression Substitute(string variable, Expression expression) => |
| 0 | 1129 | | new NaturalLog(A.Substitute(variable, expression)); |
| | 1130 | |
|
| | 1131 | | /// <summary>Standard conversion to a string representation.</summary> |
| | 1132 | | /// <returns>The string represnetation of this expression.</returns> |
| 0 | 1133 | | public override string ToString() => "ln(" + A + ")"; |
| | 1134 | | } |
| | 1135 | |
|
| | 1136 | | #endregion |
| | 1137 | |
|
| | 1138 | | #region SquareRoot |
| | 1139 | |
|
| | 1140 | | /// <summary>Represents a square root operation </summary> |
| | 1141 | | public class SquareRoot : Unary, Operation.IMathematical |
| | 1142 | | { |
| | 1143 | | /// <summary>Constructs a new square root operation.</summary> |
| | 1144 | | /// <param name="a">The expression to compute the square root of.</param> |
| 0 | 1145 | | public SquareRoot(Expression a) : base(a) { } |
| | 1146 | |
|
| | 1147 | | /// <summary>Simplifies the mathematical expression.</summary> |
| | 1148 | | /// <returns>The simplified mathematical expression.</returns> |
| | 1149 | | public override Expression Simplify() |
| 0 | 1150 | | { |
| 0 | 1151 | | Expression OPERAND = A.Simplify(); |
| | 1152 | | #region Computation |
| | 1153 | | // Rule: [A] => [B] where A is constant and B is sqrt(A) |
| 0 | 1154 | | if (OPERAND is Constant constant) |
| 0 | 1155 | | { |
| 0 | 1156 | | return constant.Simplify(this, OPERAND); |
| | 1157 | | } |
| | 1158 | | #endregion |
| 0 | 1159 | | return new SquareRoot(OPERAND); |
| 0 | 1160 | | } |
| | 1161 | |
|
| | 1162 | | internal override Expression Simplify<T>(params Expression[] operands) |
| 0 | 1163 | | { |
| 0 | 1164 | | if (operands[0] is Constant<T> a) |
| 0 | 1165 | | { |
| 0 | 1166 | | return new Constant<T>(SquareRoot(a.Value)); |
| | 1167 | | } |
| 0 | 1168 | | return base.Simplify<T>(); |
| 0 | 1169 | | } |
| | 1170 | |
|
| | 1171 | | /// <summary>Clones this expression.</summary> |
| | 1172 | | /// <returns>A clone of this expression.</returns> |
| 0 | 1173 | | public override Expression Clone() => new SquareRoot(A.Clone()); |
| | 1174 | |
|
| | 1175 | | /// <summary>Substitutes an expression for all occurences of a variable.</summary> |
| | 1176 | | /// <param name="variable">The variable to be substititued.</param> |
| | 1177 | | /// <param name="expression">The expression to substitute for each occurence of a variable.</param> |
| | 1178 | | /// <returns>The resulting expression of the substitution.</returns> |
| | 1179 | | public override Expression Substitute(string variable, Expression expression) => |
| 0 | 1180 | | new SquareRoot(A.Substitute(variable, expression)); |
| | 1181 | |
|
| | 1182 | | /// <summary>Standard conversion to a string representation.</summary> |
| | 1183 | | /// <returns>The string represnetation of this expression.</returns> |
| 0 | 1184 | | public override string ToString() => "√(" + A + ")"; |
| | 1185 | | } |
| | 1186 | |
|
| | 1187 | | #endregion |
| | 1188 | |
|
| | 1189 | | #region Exponential |
| | 1190 | |
|
| | 1191 | | /// <summary>Represents an exponential operation.</summary> |
| | 1192 | | public class Exponential : Unary, Operation.IMathematical |
| | 1193 | | { |
| | 1194 | | /// <summary>Constructs a new exponential operation.</summary> |
| | 1195 | | /// <param name="a">The expression to compute the exponetial function of.</param> |
| 0 | 1196 | | public Exponential(Expression a) : base(a) { } |
| | 1197 | |
|
| | 1198 | | /// <summary>Simplifies the mathematical expression.</summary> |
| | 1199 | | /// <returns>The simplified mathematical expression.</returns> |
| | 1200 | | public override Expression Simplify() |
| 0 | 1201 | | { |
| 0 | 1202 | | Expression OPERAND = A.Simplify(); |
| | 1203 | | #region Computation |
| | 1204 | | // Rule: [A] => [B] where A is constant and B is e ^ A |
| 0 | 1205 | | if (OPERAND is Constant constant) |
| 0 | 1206 | | { |
| 0 | 1207 | | return constant.Simplify(this, constant); |
| | 1208 | | } |
| | 1209 | | #endregion |
| 0 | 1210 | | return new Exponential(OPERAND); |
| 0 | 1211 | | } |
| | 1212 | |
|
| | 1213 | | internal override Expression Simplify<T>(params Expression[] operands) |
| 0 | 1214 | | { |
| 0 | 1215 | | if (operands[0] is Constant<T> a) |
| 0 | 1216 | | { |
| 0 | 1217 | | return new Constant<T>(Exponential(a.Value)); |
| | 1218 | | } |
| 0 | 1219 | | return base.Simplify<T>(); |
| 0 | 1220 | | } |
| | 1221 | |
|
| | 1222 | | /// <summary>Clones this expression.</summary> |
| | 1223 | | /// <returns>A clone of this expression.</returns> |
| 0 | 1224 | | public override Expression Clone() => new Exponential(A.Clone()); |
| | 1225 | |
|
| | 1226 | | /// <summary>Substitutes an expression for all occurences of a variable.</summary> |
| | 1227 | | /// <param name="variable">The variable to be substititued.</param> |
| | 1228 | | /// <param name="expression">The expression to substitute for each occurence of a variable.</param> |
| | 1229 | | /// <returns>The resulting expression of the substitution.</returns> |
| | 1230 | | public override Expression Substitute(string variable, Expression expression) => |
| 0 | 1231 | | new Exponential(A.Substitute(variable, expression)); |
| | 1232 | |
|
| | 1233 | | /// <summary>Standard conversion to a string representation.</summary> |
| | 1234 | | /// <returns>The string represnetation of this expression.</returns> |
| 0 | 1235 | | public override string ToString() => "e^(" + A + ")"; |
| | 1236 | | } |
| | 1237 | |
|
| | 1238 | | #endregion |
| | 1239 | |
|
| | 1240 | | #region Factorial |
| | 1241 | |
|
| | 1242 | | /// <summary>Represents a factorial operation.</summary> |
| | 1243 | | [RightUnaryOperator("!", OperatorPriority.Factorial)] |
| | 1244 | | public class Factorial : Unary, Operation.IMathematical |
| | 1245 | | { |
| | 1246 | | /// <summary>Constructs a new factorial operation.</summary> |
| | 1247 | | /// <param name="a">The operand of the factorial operation.</param> |
| 60 | 1248 | | public Factorial(Expression a) : base(a) { } |
| | 1249 | |
|
| | 1250 | | /// <summary>Simplifies the mathematical expression.</summary> |
| | 1251 | | /// <returns>The simplified mathematical expression.</returns> |
| | 1252 | | public override Expression Simplify() |
| 0 | 1253 | | { |
| 0 | 1254 | | Expression OPERAND = A.Simplify(); |
| | 1255 | | #region Computation |
| | 1256 | | // Rule: [A!] => [B] where A is constant and B is A! |
| 0 | 1257 | | if (OPERAND is Constant constant) |
| 0 | 1258 | | { |
| 0 | 1259 | | return constant.Simplify(this, constant); |
| | 1260 | | } |
| | 1261 | | #endregion |
| 0 | 1262 | | return new Factorial(OPERAND); |
| 0 | 1263 | | } |
| | 1264 | |
|
| | 1265 | | internal override Expression Simplify<T>(params Expression[] operands) |
| 0 | 1266 | | { |
| 0 | 1267 | | if (operands[0] is Constant<T> a) |
| 0 | 1268 | | { |
| 0 | 1269 | | return new Constant<T>(Factorial(a.Value)); |
| | 1270 | | } |
| 0 | 1271 | | return base.Simplify<T>(); |
| 0 | 1272 | | } |
| | 1273 | |
|
| | 1274 | | /// <summary>Clones this expression.</summary> |
| | 1275 | | /// <returns>A clone of this expression.</returns> |
| 0 | 1276 | | public override Expression Clone() => new Factorial(A.Clone()); |
| | 1277 | |
|
| | 1278 | | /// <summary>Substitutes an expression for all occurences of a variable.</summary> |
| | 1279 | | /// <param name="variable">The variable to be substititued.</param> |
| | 1280 | | /// <param name="expression">The expression to substitute for each occurence of a variable.</param> |
| | 1281 | | /// <returns>The resulting expression of the substitution.</returns> |
| | 1282 | | public override Expression Substitute(string variable, Expression expression) => |
| 0 | 1283 | | new Factorial(A.Substitute(variable, expression)); |
| | 1284 | |
|
| | 1285 | | /// <summary>Standard conversion to a string representation.</summary> |
| | 1286 | | /// <returns>The string represnetation of this expression.</returns> |
| | 1287 | | public override string ToString() |
| 20 | 1288 | | { |
| | 1289 | | static bool RequiresParentheses(string expressionString) |
| 20 | 1290 | | { |
| 100 | 1291 | | foreach (char c in expressionString) |
| 20 | 1292 | | { |
| 20 | 1293 | | if (!char.IsDigit(c) && c != '.') |
| 0 | 1294 | | { |
| 0 | 1295 | | return true; |
| | 1296 | | } |
| 20 | 1297 | | } |
| 20 | 1298 | | return false; |
| 20 | 1299 | | } |
| | 1300 | |
|
| 20 | 1301 | | string? a = A.ToString(); |
| 20 | 1302 | | if (a is not null && RequiresParentheses(a)) |
| 0 | 1303 | | { |
| 0 | 1304 | | a = "(" + a + ")"; |
| 0 | 1305 | | } |
| 20 | 1306 | | return a + "!"; |
| 20 | 1307 | | } |
| | 1308 | | } |
| | 1309 | |
|
| | 1310 | | #endregion |
| | 1311 | |
|
| | 1312 | | #region Invert |
| | 1313 | |
|
| | 1314 | | /// <summary>Represents a reciprical/invert operation.</summary> |
| | 1315 | | public class Invert : Unary, Operation.IMathematical |
| | 1316 | | { |
| | 1317 | | /// <summary>Constructs a new reciprical/invert operation.</summary> |
| | 1318 | | /// <param name="a">Teh expression to recipricate/invert.</param> |
| 0 | 1319 | | public Invert(Expression a) : base(a) { } |
| | 1320 | |
|
| | 1321 | | /// <summary>Simplifies the mathematical expression.</summary> |
| | 1322 | | /// <returns>The simplified mathematical expression.</returns> |
| | 1323 | | public override Expression Simplify() |
| 0 | 1324 | | { |
| 0 | 1325 | | Expression OPERAND = A.Simplify(); |
| | 1326 | | #region Computation |
| | 1327 | | // Rule: [A] => [B] where A is constant and B is 1 / A |
| 0 | 1328 | | if (OPERAND is Constant constant) |
| 0 | 1329 | | { |
| 0 | 1330 | | return constant.Simplify(this, OPERAND); |
| | 1331 | | } |
| | 1332 | | #endregion |
| 0 | 1333 | | return new Invert(OPERAND); |
| 0 | 1334 | | } |
| | 1335 | |
|
| | 1336 | | internal override Expression Simplify<T>(params Expression[] operands) |
| 0 | 1337 | | { |
| 0 | 1338 | | if (operands[0] is Constant<T> a) |
| 0 | 1339 | | { |
| 0 | 1340 | | return new Constant<T>(Inversion(a.Value)); |
| | 1341 | | } |
| 0 | 1342 | | return base.Simplify<T>(); |
| 0 | 1343 | | } |
| | 1344 | |
|
| | 1345 | | /// <summary>Clones this expression.</summary> |
| | 1346 | | /// <returns>A clone of this expression.</returns> |
| 0 | 1347 | | public override Expression Clone() => new Invert(A.Clone()); |
| | 1348 | |
|
| | 1349 | | /// <summary>Substitutes an expression for all occurences of a variable.</summary> |
| | 1350 | | /// <param name="variable">The variable to be substititued.</param> |
| | 1351 | | /// <param name="expression">The expression to substitute for each occurence of a variable.</param> |
| | 1352 | | /// <returns>The resulting expression of the substitution.</returns> |
| | 1353 | | public override Expression Substitute(string variable, Expression expression) => |
| 0 | 1354 | | new Invert(A.Substitute(variable, expression)); |
| | 1355 | |
|
| | 1356 | | /// <summary>Standard conversion to a string representation.</summary> |
| | 1357 | | /// <returns>The string represnetation of this expression.</returns> |
| 0 | 1358 | | public override string ToString() => "(1 / " + A + ")"; |
| | 1359 | | } |
| | 1360 | |
|
| | 1361 | | #endregion |
| | 1362 | |
|
| | 1363 | | #region Trigonometry + Inheriters |
| | 1364 | |
|
| | 1365 | | #region Trigonometry |
| | 1366 | |
|
| | 1367 | | /// <summary>Represents one of the trigonometry functions.</summary> |
| | 1368 | | public abstract class Trigonometry : Unary, Operation.IMathematical |
| | 1369 | | { |
| | 1370 | | /// <summary>Constructs a new trigonometry expression.</summary> |
| | 1371 | | /// <param name="a">The parameter to the trigonometry expression.</param> |
| 0 | 1372 | | public Trigonometry(Expression a) : base(a) { } |
| | 1373 | | } |
| | 1374 | |
|
| | 1375 | | #endregion |
| | 1376 | |
|
| | 1377 | | #region Sine |
| | 1378 | |
|
| | 1379 | | /// <summary>Represents the sine trigonometric function.</summary> |
| | 1380 | | public class Sine : Trigonometry, Operation.IMathematical |
| | 1381 | | { |
| | 1382 | | /// <summary>Constructs a new sine expression.</summary> |
| | 1383 | | /// <param name="a">The parameter to the sine expression.</param> |
| 0 | 1384 | | public Sine(Expression a) : base(a) { } |
| | 1385 | |
|
| | 1386 | | /// <summary>Simplifies the mathematical expression.</summary> |
| | 1387 | | /// <returns>The simplified mathematical expression.</returns> |
| | 1388 | | public override Expression Simplify() |
| 0 | 1389 | | { |
| 0 | 1390 | | Expression OPERAND = A.Simplify(); |
| | 1391 | |
|
| 0 | 1392 | | return new Sine(OPERAND); |
| 0 | 1393 | | } |
| | 1394 | |
|
| | 1395 | | internal override Expression Simplify<T>(params Expression[] operands) |
| 0 | 1396 | | { |
| | 1397 | | #warning TODO |
| | 1398 | | // if (A is Constant<T> a) |
| | 1399 | | // { |
| | 1400 | | // //return new Constant<T>(Compute.Sine(a.Value)); |
| | 1401 | | // } |
| 0 | 1402 | | return base.Simplify<T>(); |
| 0 | 1403 | | } |
| | 1404 | |
|
| | 1405 | | /// <summary>Clones this expression.</summary> |
| | 1406 | | /// <returns>A clone of this expression.</returns> |
| 0 | 1407 | | public override Expression Clone() => new Sine(A.Clone()); |
| | 1408 | |
|
| | 1409 | | /// <summary>Substitutes an expression for all occurences of a variable.</summary> |
| | 1410 | | /// <param name="variable">The variable to be substititued.</param> |
| | 1411 | | /// <param name="expression">The expression to substitute for each occurence of a variable.</param> |
| | 1412 | | /// <returns>The resulting expression of the substitution.</returns> |
| | 1413 | | public override Expression Substitute(string variable, Expression expression) => |
| 0 | 1414 | | new Sine(A.Substitute(variable, expression)); |
| | 1415 | |
|
| | 1416 | | /// <summary>Standard conversion to a string representation.</summary> |
| | 1417 | | /// <returns>The string represnetation of this expression.</returns> |
| 0 | 1418 | | public override string ToString() => nameof(Sine) + "(" + A + ")"; |
| | 1419 | | } |
| | 1420 | |
|
| | 1421 | | #endregion |
| | 1422 | |
|
| | 1423 | | #region Cosine |
| | 1424 | |
|
| | 1425 | | /// <summary>Represents the cosine trigonometric function.</summary> |
| | 1426 | | public class Cosine : Trigonometry, Operation.IMathematical |
| | 1427 | | { |
| | 1428 | | /// <summary>Constructs a new cosine expression.</summary> |
| | 1429 | | /// <param name="a">The parameter to the cosine expression.</param> |
| 0 | 1430 | | public Cosine(Expression a) : base(a) { } |
| | 1431 | |
|
| | 1432 | | /// <summary>Simplifies the mathematical expression.</summary> |
| | 1433 | | /// <returns>The simplified mathematical expression.</returns> |
| | 1434 | | public override Expression Simplify() |
| 0 | 1435 | | { |
| 0 | 1436 | | Expression OPERAND = A.Simplify(); |
| | 1437 | |
|
| 0 | 1438 | | return new Cosine(OPERAND); |
| 0 | 1439 | | } |
| | 1440 | |
|
| | 1441 | | internal override Expression Simplify<T>(params Expression[] operands) |
| 0 | 1442 | | { |
| | 1443 | | #warning TODO |
| | 1444 | | // if (A is Constant<T> a) |
| | 1445 | | // { |
| | 1446 | | // //return new Constant<T>(Compute.Cosine(a.Value)); |
| | 1447 | | // } |
| 0 | 1448 | | return base.Simplify<T>(); |
| 0 | 1449 | | } |
| | 1450 | |
|
| | 1451 | | /// <summary>Clones this expression.</summary> |
| | 1452 | | /// <returns>A clone of this expression.</returns> |
| 0 | 1453 | | public override Expression Clone() => new Cosine(A.Clone()); |
| | 1454 | |
|
| | 1455 | | /// <summary>Substitutes an expression for all occurences of a variable.</summary> |
| | 1456 | | /// <param name="variable">The variable to be substititued.</param> |
| | 1457 | | /// <param name="expression">The expression to substitute for each occurence of a variable.</param> |
| | 1458 | | /// <returns>The resulting expression of the substitution.</returns> |
| | 1459 | | public override Expression Substitute(string variable, Expression expression) => |
| 0 | 1460 | | new Cosine(A.Substitute(variable, expression)); |
| | 1461 | |
|
| | 1462 | | /// <summary>Standard conversion to a string representation.</summary> |
| | 1463 | | /// <returns>The string represnetation of this expression.</returns> |
| 0 | 1464 | | public override string ToString() => nameof(Cosine) + "(" + A + ")"; |
| | 1465 | | } |
| | 1466 | |
|
| | 1467 | | #endregion |
| | 1468 | |
|
| | 1469 | | #region Tangent |
| | 1470 | |
|
| | 1471 | | /// <summary>Represents the tanget trigonometric function.</summary> |
| | 1472 | | public class Tangent : Trigonometry, Operation.IMathematical |
| | 1473 | | { |
| | 1474 | | /// <summary>Constructs a new tangent expression.</summary> |
| | 1475 | | /// <param name="a">The parameter to the tangent expression.</param> |
| 0 | 1476 | | public Tangent(Expression a) : base(a) { } |
| | 1477 | |
|
| | 1478 | | /// <summary>Simplifies the mathematical expression.</summary> |
| | 1479 | | /// <returns>The simplified mathematical expression.</returns> |
| | 1480 | | public override Expression Simplify() |
| 0 | 1481 | | { |
| 0 | 1482 | | Expression OPERAND = A.Simplify(); |
| | 1483 | |
|
| 0 | 1484 | | return new Tangent(OPERAND); |
| 0 | 1485 | | } |
| | 1486 | |
|
| | 1487 | | internal override Expression Simplify<T>(params Expression[] operands) |
| 0 | 1488 | | { |
| | 1489 | | #warning TODO |
| | 1490 | | // if (A is Constant<T> a) |
| | 1491 | | // { |
| | 1492 | | // //return new Constant<T>(Compute.Tanget(a.Value)); |
| | 1493 | | // } |
| 0 | 1494 | | return base.Simplify<T>(); |
| 0 | 1495 | | } |
| | 1496 | |
|
| | 1497 | | /// <summary>Clones this expression.</summary> |
| | 1498 | | /// <returns>A clone of this expression.</returns> |
| 0 | 1499 | | public override Expression Clone() => new Tangent(A.Clone()); |
| | 1500 | |
|
| | 1501 | | /// <summary>Substitutes an expression for all occurences of a variable.</summary> |
| | 1502 | | /// <param name="variable">The variable to be substititued.</param> |
| | 1503 | | /// <param name="expression">The expression to substitute for each occurence of a variable.</param> |
| | 1504 | | /// <returns>The resulting expression of the substitution.</returns> |
| | 1505 | | public override Expression Substitute(string variable, Expression expression) => |
| 0 | 1506 | | new Tangent(A.Substitute(variable, expression)); |
| | 1507 | |
|
| | 1508 | | /// <summary>Standard conversion to a string representation.</summary> |
| | 1509 | | /// <returns>The string represnetation of this expression.</returns> |
| 0 | 1510 | | public override string ToString() => nameof(Tangent) + "(" + A + ")"; |
| | 1511 | | } |
| | 1512 | |
|
| | 1513 | | #endregion |
| | 1514 | |
|
| | 1515 | | #region Cosecant |
| | 1516 | |
|
| | 1517 | | /// <summary>Represents the cosecant trigonometric function.</summary> |
| | 1518 | | public class Cosecant : Trigonometry, Operation.IMathematical |
| | 1519 | | { |
| | 1520 | | /// <summary>Constructs a new cosecant expression.</summary> |
| | 1521 | | /// <param name="a">The parameter to the cosecant expression.</param> |
| 0 | 1522 | | public Cosecant(Expression a) : base(a) { } |
| | 1523 | |
|
| | 1524 | | /// <summary>Simplifies the mathematical expression.</summary> |
| | 1525 | | /// <returns>The simplified mathematical expression.</returns> |
| | 1526 | | public override Expression Simplify() |
| 0 | 1527 | | { |
| 0 | 1528 | | Expression OPERAND = A.Simplify(); |
| | 1529 | |
|
| 0 | 1530 | | return new Cosecant(OPERAND); |
| 0 | 1531 | | } |
| | 1532 | |
|
| | 1533 | | internal override Expression Simplify<T>(params Expression[] operands) |
| 0 | 1534 | | { |
| | 1535 | | #warning TODO |
| | 1536 | | // if (A is Constant<T> a) |
| | 1537 | | // { |
| | 1538 | | // //return new Constant<T>(Compute.Cosecant(a.Value)); |
| | 1539 | | // } |
| 0 | 1540 | | return base.Simplify<T>(); |
| 0 | 1541 | | } |
| | 1542 | |
|
| | 1543 | | /// <summary>Clones this expression.</summary> |
| | 1544 | | /// <returns>A clone of this expression.</returns> |
| 0 | 1545 | | public override Expression Clone() => new Cosecant(A.Clone()); |
| | 1546 | |
|
| | 1547 | | /// <summary>Substitutes an expression for all occurences of a variable.</summary> |
| | 1548 | | /// <param name="variable">The variable to be substititued.</param> |
| | 1549 | | /// <param name="expression">The expression to substitute for each occurence of a variable.</param> |
| | 1550 | | /// <returns>The resulting expression of the substitution.</returns> |
| | 1551 | | public override Expression Substitute(string variable, Expression expression) => |
| 0 | 1552 | | new Cosecant(A.Substitute(variable, expression)); |
| | 1553 | |
|
| | 1554 | | /// <summary>Standard conversion to a string representation.</summary> |
| | 1555 | | /// <returns>The string represnetation of this expression.</returns> |
| 0 | 1556 | | public override string ToString() => nameof(Cosecant) + "(" + A + ")"; |
| | 1557 | | } |
| | 1558 | |
|
| | 1559 | | #endregion |
| | 1560 | |
|
| | 1561 | | #region Secant |
| | 1562 | |
|
| | 1563 | | /// <summary>Represents the secant trigonometric function.</summary> |
| | 1564 | | public class Secant : Trigonometry, Operation.IMathematical |
| | 1565 | | { |
| | 1566 | | /// <summary>Constructs a new secant expression.</summary> |
| | 1567 | | /// <param name="a">The parameter to the secant expression.</param> |
| 0 | 1568 | | public Secant(Expression a) : base(a) { } |
| | 1569 | |
|
| | 1570 | | /// <summary>Simplifies the mathematical expression.</summary> |
| | 1571 | | /// <returns>The simplified mathematical expression.</returns> |
| | 1572 | | public override Expression Simplify() |
| 0 | 1573 | | { |
| 0 | 1574 | | Expression OPERAND = A.Simplify(); |
| | 1575 | |
|
| 0 | 1576 | | return new Secant(OPERAND); |
| 0 | 1577 | | } |
| | 1578 | |
|
| | 1579 | | internal override Expression Simplify<T>(params Expression[] operands) |
| 0 | 1580 | | { |
| | 1581 | | #warning TODO |
| | 1582 | | // if (A is Constant<T> a) |
| | 1583 | | // { |
| | 1584 | | // //return new Constant<T>(Compute.Secant(a.Value)); |
| | 1585 | | // } |
| 0 | 1586 | | return base.Simplify<T>(); |
| 0 | 1587 | | } |
| | 1588 | |
|
| | 1589 | | /// <summary>Clones this expression.</summary> |
| | 1590 | | /// <returns>A clone of this expression.</returns> |
| 0 | 1591 | | public override Expression Clone() => new Secant(A.Clone()); |
| | 1592 | |
|
| | 1593 | | /// <summary>Substitutes an expression for all occurences of a variable.</summary> |
| | 1594 | | /// <param name="variable">The variable to be substititued.</param> |
| | 1595 | | /// <param name="expression">The expression to substitute for each occurence of a variable.</param> |
| | 1596 | | /// <returns>The resulting expression of the substitution.</returns> |
| | 1597 | | public override Expression Substitute(string variable, Expression expression) => |
| 0 | 1598 | | new Secant(A.Substitute(variable, expression)); |
| | 1599 | |
|
| | 1600 | | /// <summary>Standard conversion to a string representation.</summary> |
| | 1601 | | /// <returns>The string represnetation of this expression.</returns> |
| 0 | 1602 | | public override string ToString() => nameof(Secant) + "(" + A + ")"; |
| | 1603 | | } |
| | 1604 | |
|
| | 1605 | | #endregion |
| | 1606 | |
|
| | 1607 | | #region Cotangent |
| | 1608 | |
|
| | 1609 | | /// <summary>Represents the cotangent trigonometric function.</summary> |
| | 1610 | | public class Cotangent : Trigonometry, Operation.IMathematical |
| | 1611 | | { |
| | 1612 | | /// <summary>Constructs a new cotangent expression.</summary> |
| | 1613 | | /// <param name="a">The parameter to the cotangent expression.</param> |
| 0 | 1614 | | public Cotangent(Expression a) : base(a) { } |
| | 1615 | |
|
| | 1616 | | /// <summary>Simplifies the mathematical expression.</summary> |
| | 1617 | | /// <returns>The simplified mathematical expression.</returns> |
| | 1618 | | public override Expression Simplify() |
| 0 | 1619 | | { |
| 0 | 1620 | | Expression OPERAND = A.Simplify(); |
| | 1621 | |
|
| 0 | 1622 | | return new Cotangent(OPERAND); |
| 0 | 1623 | | } |
| | 1624 | |
|
| | 1625 | | internal override Expression Simplify<T>(params Expression[] operands) |
| 0 | 1626 | | { |
| | 1627 | | #warning TODO |
| | 1628 | | // if (A is Constant<T> a) |
| | 1629 | | // { |
| | 1630 | | // //return new Constant<T>(Compute.Cotangent(a.Value)); |
| | 1631 | | // } |
| 0 | 1632 | | return base.Simplify<T>(); |
| 0 | 1633 | | } |
| | 1634 | |
|
| | 1635 | | /// <summary>Clones this expression.</summary> |
| | 1636 | | /// <returns>A clone of this expression.</returns> |
| 0 | 1637 | | public override Expression Clone() => new Cotangent(A.Clone()); |
| | 1638 | |
|
| | 1639 | | /// <summary>Substitutes an expression for all occurences of a variable.</summary> |
| | 1640 | | /// <param name="variable">The variable to be substititued.</param> |
| | 1641 | | /// <param name="expression">The expression to substitute for each occurence of a variable.</param> |
| | 1642 | | /// <returns>The resulting expression of the substitution.</returns> |
| | 1643 | | public override Expression Substitute(string variable, Expression expression) => |
| 0 | 1644 | | new Cotangent(A.Substitute(variable, expression)); |
| | 1645 | |
|
| | 1646 | | /// <summary>Standard conversion to a string representation.</summary> |
| | 1647 | | /// <returns>The string represnetation of this expression.</returns> |
| 0 | 1648 | | public override string ToString() => nameof(Cotangent) + "(" + A + ")"; |
| | 1649 | | } |
| | 1650 | |
|
| | 1651 | | #endregion |
| | 1652 | |
|
| | 1653 | | #endregion |
| | 1654 | |
|
| | 1655 | | #endregion |
| | 1656 | |
|
| | 1657 | | #region Binary + Inheriters |
| | 1658 | |
|
| | 1659 | | #region Binary |
| | 1660 | |
|
| | 1661 | | #pragma warning disable CS0659 // Type overrides Object.Equals(object o) but does not override Object.GetHashCode() |
| | 1662 | | /// <summary>Abstract base class for all symbolic mathematics binary operations.</summary> |
| | 1663 | | public abstract class Binary : Operation |
| | 1664 | | #pragma warning restore CS0659 // Type overrides Object.Equals(object o) but does not override Object.GetHashCode() |
| | 1665 | | { |
| | 1666 | | /// <summary>The first operand of the binary operation.</summary> |
| 606 | 1667 | | public Expression A { get; set; } |
| | 1668 | | /// <summary>The second operand of the binary operation.</summary> |
| 612 | 1669 | | public Expression B { get; set; } |
| | 1670 | |
|
| | 1671 | | /// <summary>Constructs a new binary operation.</summary> |
| | 1672 | | /// <param name="a">The left operand of the binary operation.</param> |
| | 1673 | | /// <param name="b">The right operand of the binary operation.</param> |
| 132 | 1674 | | public Binary(Expression a, Expression b) |
| 132 | 1675 | | { |
| 132 | 1676 | | A = a; |
| 132 | 1677 | | B = b; |
| 132 | 1678 | | } |
| | 1679 | |
|
| | 1680 | | /// <summary>Standard equality check.</summary> |
| | 1681 | | /// <param name="b">The object to check for equality with.</param> |
| | 1682 | | /// <returns>True if equal. False if not.</returns> |
| | 1683 | | public override bool Equals(object? b) |
| 54 | 1684 | | { |
| 54 | 1685 | | if (b is not null && GetType() == b.GetType()) |
| 54 | 1686 | | { |
| 54 | 1687 | | return A.Equals(((Binary)b).A) && B.Equals(((Binary)b).B); |
| | 1688 | | } |
| 0 | 1689 | | return false; |
| 54 | 1690 | | } |
| | 1691 | | } |
| | 1692 | |
|
| | 1693 | | #endregion |
| | 1694 | |
|
| | 1695 | | #region AddOrSubtract + Inheriters |
| | 1696 | |
|
| | 1697 | | #region AddOrSubtract |
| | 1698 | |
|
| | 1699 | | /// <summary>Represents an addition or a subtraction operation.</summary> |
| | 1700 | | public abstract class AddOrSubtract : Binary, Operation.IMathematical |
| | 1701 | | { |
| | 1702 | | /// <summary>Constructs a new addition or subtraction operation.</summary> |
| | 1703 | | /// <param name="a">The left operand of the operation.</param> |
| | 1704 | | /// <param name="b">The right operand of the operation.</param> |
| 168 | 1705 | | public AddOrSubtract(Expression a, Expression b) : base(a, b) { } |
| | 1706 | | } |
| | 1707 | |
|
| | 1708 | | #endregion |
| | 1709 | |
|
| | 1710 | | #region Add |
| | 1711 | |
|
| | 1712 | | /// <summary>Represents an addition operation.</summary> |
| | 1713 | | [BinaryOperator("+", OperatorPriority.Addition)] |
| | 1714 | | public class Add : AddOrSubtract |
| | 1715 | | { |
| | 1716 | | /// <summary>Constructs a new addition operation.</summary> |
| | 1717 | | /// <param name="a">The left operand of the addition operation.</param> |
| | 1718 | | /// <param name="b">The right operand of the addition operation.</param> |
| 90 | 1719 | | public Add(Expression a, Expression b) : base(a, b) { } |
| | 1720 | |
|
| | 1721 | | /// <summary>Simplifies the mathematical expression.</summary> |
| | 1722 | | /// <returns>The simplified mathematical expression.</returns> |
| | 1723 | | public override Expression Simplify() |
| 0 | 1724 | | { |
| 0 | 1725 | | Expression LEFT = A.Simplify(); |
| 0 | 1726 | | Expression RIGHT = B.Simplify(); |
| | 1727 | | #region Computation |
| 0 | 1728 | | { // Rule: [A + B] => [C] where A is constant, B is constant, and C is A + B |
| 0 | 1729 | | if (LEFT is Constant A && RIGHT is Constant B) |
| 0 | 1730 | | { |
| 0 | 1731 | | return A.Simplify(this, A, B); |
| | 1732 | | } |
| 0 | 1733 | | } |
| | 1734 | | #endregion |
| | 1735 | | #region Additive Identity Property |
| 0 | 1736 | | { // Rule: [X + 0] => [X] |
| 0 | 1737 | | if (RIGHT is Constant right && right.IsZero) |
| 0 | 1738 | | { |
| 0 | 1739 | | return LEFT; |
| | 1740 | | } |
| 0 | 1741 | | } |
| 0 | 1742 | | { // Rule: [0 + X] => [X] |
| 0 | 1743 | | if (LEFT is Constant left && left.IsZero) |
| 0 | 1744 | | { |
| 0 | 1745 | | return RIGHT; |
| | 1746 | | } |
| 0 | 1747 | | } |
| | 1748 | | #endregion |
| | 1749 | | #region Commutative/Associative Property |
| 0 | 1750 | | { // Rule: ['X + A' + B] => [X + C] where A is constant, B is constant, and C is A + B |
| 0 | 1751 | | if (LEFT is Add ADD && ADD.B is Constant A && RIGHT is Constant B) |
| 0 | 1752 | | { |
| 0 | 1753 | | var C = (A + B).Simplify(); |
| 0 | 1754 | | var X = ADD.A; |
| 0 | 1755 | | return X + C; |
| | 1756 | | } |
| 0 | 1757 | | } |
| 0 | 1758 | | { // Rule: ['A + X' + B] => [X + C] where A is constant, B is constant, and C is A + B |
| 0 | 1759 | | if (LEFT is Add ADD && ADD.A is Constant A && RIGHT is Constant B) |
| 0 | 1760 | | { |
| 0 | 1761 | | var C = (A + B).Simplify(); |
| 0 | 1762 | | var X = ADD.B; |
| 0 | 1763 | | return X + C; |
| | 1764 | | } |
| 0 | 1765 | | } |
| 0 | 1766 | | { // Rule: [B + 'X + A'] => [X + C] where A is constant, B is constant, and C is A + B |
| 0 | 1767 | | if (RIGHT is Add ADD && ADD.B is Constant A && LEFT is Constant B) |
| 0 | 1768 | | { |
| 0 | 1769 | | var C = (A + B).Simplify(); |
| 0 | 1770 | | var X = ADD.A; |
| 0 | 1771 | | return X + C; |
| | 1772 | | } |
| 0 | 1773 | | } |
| 0 | 1774 | | { // Rule: [B + 'A + X'] => [X + C] where A is constant, B is constant, and C is A + B |
| 0 | 1775 | | if (RIGHT is Add ADD && ADD.A is Constant A && LEFT is Constant B) |
| 0 | 1776 | | { |
| 0 | 1777 | | var C = (A + B).Simplify(); |
| 0 | 1778 | | var X = ADD.B; |
| 0 | 1779 | | return X + C; |
| | 1780 | | } |
| 0 | 1781 | | } |
| 0 | 1782 | | { // Rule: ['X - A' + B] => [X + C] where A is constant, B is constant, and C is B - A |
| 0 | 1783 | | if (LEFT is Subtract SUBTRACT && SUBTRACT.B is Constant A && RIGHT is Constant B) |
| 0 | 1784 | | { |
| 0 | 1785 | | var C = (B - A).Simplify(); |
| 0 | 1786 | | var X = SUBTRACT.A; |
| 0 | 1787 | | return X + C; |
| | 1788 | | } |
| 0 | 1789 | | } |
| 0 | 1790 | | { // Rule: ['A - X' + B] => [C - X] where A is constant, B is constant, and C is A + B |
| 0 | 1791 | | if (LEFT is Subtract SUBTRACT && SUBTRACT.A is Constant A && RIGHT is Constant B) |
| 0 | 1792 | | { |
| 0 | 1793 | | var C = (A + B).Simplify(); |
| 0 | 1794 | | var X = SUBTRACT.B; |
| 0 | 1795 | | return C - X; |
| | 1796 | | } |
| 0 | 1797 | | } |
| 0 | 1798 | | { // Rule: [B + 'X - A'] => [X + C] where A is constant, B is constant, and C is B - A |
| 0 | 1799 | | if (RIGHT is Subtract SUBTRACT && SUBTRACT.B is Constant A && LEFT is Constant B) |
| 0 | 1800 | | { |
| 0 | 1801 | | var C = (B - A).Simplify(); |
| 0 | 1802 | | var X = SUBTRACT.A; |
| 0 | 1803 | | return C + X; |
| | 1804 | | } |
| 0 | 1805 | | } |
| 0 | 1806 | | { // Rule: [B + 'A - X'] => [C - X] where A is constant, B is constant, and C is A + B |
| 0 | 1807 | | if (RIGHT is Subtract SUBTRACT && SUBTRACT.A is Constant A && LEFT is Constant B) |
| 0 | 1808 | | { |
| 0 | 1809 | | var C = (A + B).Simplify(); |
| 0 | 1810 | | var X = SUBTRACT.B; |
| 0 | 1811 | | return C - X; |
| | 1812 | | } |
| 0 | 1813 | | } |
| | 1814 | | #endregion |
| 0 | 1815 | | return LEFT + RIGHT; |
| 0 | 1816 | | } |
| | 1817 | |
|
| | 1818 | | internal override Expression Simplify<T>(params Expression[] operands) |
| 0 | 1819 | | { |
| 0 | 1820 | | if (operands[0] is Constant<T> a && operands[1] is Constant<T> b) |
| 0 | 1821 | | { |
| 0 | 1822 | | return new Constant<T>(Addition(a.Value, b.Value)); |
| | 1823 | | } |
| 0 | 1824 | | return base.Simplify<T>(); |
| 0 | 1825 | | } |
| | 1826 | |
|
| | 1827 | | /// <summary>Clones this expression.</summary> |
| | 1828 | | /// <returns>A clone of this expression.</returns> |
| 0 | 1829 | | public override Expression Clone() => new Add(A.Clone(), B.Clone()); |
| | 1830 | |
|
| | 1831 | | /// <summary>Substitutes an expression for all occurences of a variable.</summary> |
| | 1832 | | /// <param name="variable">The variable to be substititued.</param> |
| | 1833 | | /// <param name="expression">The expression to substitute for each occurence of a variable.</param> |
| | 1834 | | /// <returns>The resulting expression of the substitution.</returns> |
| | 1835 | | public override Expression Substitute(string variable, Expression expression) => |
| 0 | 1836 | | new Add(A.Substitute(variable, expression), B.Substitute(variable, expression)); |
| | 1837 | |
|
| | 1838 | | /// <summary>Standard conversion to a string representation.</summary> |
| | 1839 | | /// <returns>The string represnetation of this expression.</returns> |
| | 1840 | | public override string ToString() |
| 30 | 1841 | | { |
| 30 | 1842 | | string? a = A.ToString(); |
| 30 | 1843 | | string? b = B.ToString(); |
| 30 | 1844 | | { |
| 30 | 1845 | | if ((A is Multiply || A is Divide) && A is Constant CONSTANT && CONSTANT.IsNegative) |
| 0 | 1846 | | { |
| 0 | 1847 | | a = "(" + a + ")"; |
| 0 | 1848 | | } |
| 30 | 1849 | | } |
| 30 | 1850 | | { |
| 30 | 1851 | | if (B is Add || B is Subtract || A is Multiply || A is Divide) |
| 8 | 1852 | | { |
| 8 | 1853 | | b = "(" + b + ")"; |
| 8 | 1854 | | } |
| 30 | 1855 | | } |
| 30 | 1856 | | { |
| 30 | 1857 | | if (B is Constant CONSTANT && CONSTANT.IsNegative) |
| 0 | 1858 | | { |
| 0 | 1859 | | return a + " - " + Negation(B as Constant); |
| | 1860 | | } |
| 30 | 1861 | | } |
| 30 | 1862 | | return a + " + " + b; |
| 30 | 1863 | | } |
| | 1864 | | } |
| | 1865 | |
|
| | 1866 | | #endregion |
| | 1867 | |
|
| | 1868 | | #region Subtract |
| | 1869 | |
|
| | 1870 | | /// <summary>Represents a subtraction operation.</summary> |
| | 1871 | | [BinaryOperator("-", OperatorPriority.Subtraction)] |
| | 1872 | | public class Subtract : AddOrSubtract |
| | 1873 | | { |
| | 1874 | | /// <summary>Constructs a new subtraction operation.</summary> |
| | 1875 | | /// <param name="a">The left operand of the subtraction operation.</param> |
| | 1876 | | /// <param name="b">The right operand of the subtraction operation.</param> |
| 78 | 1877 | | public Subtract(Expression a, Expression b) : base(a, b) { } |
| | 1878 | |
|
| | 1879 | | /// <summary>Simplifies the mathematical expression.</summary> |
| | 1880 | | /// <returns>The simplified mathematical expression.</returns> |
| | 1881 | | public override Expression Simplify() |
| 0 | 1882 | | { |
| 0 | 1883 | | Expression LEFT = A.Simplify(); |
| 0 | 1884 | | Expression RIGHT = B.Simplify(); |
| | 1885 | | #region Computation |
| 0 | 1886 | | { // Rule: [A - B] => [C] where A is constant, B is constant, and C is A - B |
| 0 | 1887 | | if (LEFT is Constant left && RIGHT is Constant right) |
| 0 | 1888 | | { |
| 0 | 1889 | | return left.Simplify(this, left, right); |
| | 1890 | | } |
| 0 | 1891 | | } |
| | 1892 | | #endregion |
| | 1893 | | #region Identity Property |
| 0 | 1894 | | { // Rule: [X - 0] => [X] |
| 0 | 1895 | | if (RIGHT is Constant right && right.IsZero) |
| 0 | 1896 | | { |
| 0 | 1897 | | return LEFT; |
| | 1898 | | } |
| 0 | 1899 | | } |
| 0 | 1900 | | { // Rule: [0 - X] => [-X] |
| 0 | 1901 | | if (LEFT is Constant left && left.IsZero) |
| 0 | 1902 | | { |
| 0 | 1903 | | return new Negate(RIGHT); |
| | 1904 | | } |
| 0 | 1905 | | } |
| | 1906 | | #endregion |
| | 1907 | | #region Commutative/Associative Property |
| 0 | 1908 | | { // Rule: ['X - A' - B] => [X - C] where A is constant, B is constant, and C is A + B |
| 0 | 1909 | | if (LEFT is Subtract SUBTRACT && SUBTRACT.B is Constant A && RIGHT is Constant B) |
| 0 | 1910 | | { |
| 0 | 1911 | | var C = (A + B).Simplify(); |
| 0 | 1912 | | var X = SUBTRACT.A; |
| 0 | 1913 | | return X - C; |
| | 1914 | | } |
| 0 | 1915 | | } |
| 0 | 1916 | | { // Rule: ['A - X' - B] => [C - X] where A is constant, B is constant, and C is A - B |
| 0 | 1917 | | if (LEFT is Subtract SUBTRACT && SUBTRACT.A is Constant A && RIGHT is Constant B) |
| 0 | 1918 | | { |
| 0 | 1919 | | var C = (A - B).Simplify(); |
| 0 | 1920 | | var X = SUBTRACT.B; |
| 0 | 1921 | | return C - X; |
| | 1922 | | } |
| 0 | 1923 | | } |
| 0 | 1924 | | { // Rule: [B - 'X - A'] => [C - X] where A is constant, B is constant, and C is B - A |
| 0 | 1925 | | if (RIGHT is Subtract SUBTRACT && SUBTRACT.B is Constant A && LEFT is Constant B) |
| 0 | 1926 | | { |
| 0 | 1927 | | var C = (B - A).Simplify(); |
| 0 | 1928 | | var X = SUBTRACT.A; |
| 0 | 1929 | | return C - X; |
| | 1930 | | } |
| 0 | 1931 | | } |
| 0 | 1932 | | { // Rule: [B - 'A - X'] => [C - X] where A is constant, B is constant, and C is B - A |
| 0 | 1933 | | if (RIGHT is Subtract SUBTRACT && SUBTRACT.A is Constant A && LEFT is Constant B) |
| 0 | 1934 | | { |
| 0 | 1935 | | var C = (B - A).Simplify(); |
| 0 | 1936 | | var X = SUBTRACT.A; |
| 0 | 1937 | | return C - X; |
| | 1938 | | } |
| 0 | 1939 | | } |
| 0 | 1940 | | { // Rule: ['X + A' - B] => [X + C] where A is constant, B is constant, and C is A - B |
| 0 | 1941 | | if (LEFT is Add ADD && ADD.B is Constant A && RIGHT is Constant B) |
| 0 | 1942 | | { |
| 0 | 1943 | | var C = (A - B).Simplify(); |
| 0 | 1944 | | var X = ADD.A; |
| 0 | 1945 | | return X + C; |
| | 1946 | | } |
| 0 | 1947 | | } |
| 0 | 1948 | | { // Rule: ['A + X' - B] => [C + X] where A is constant, B is constant, and C is A - B |
| 0 | 1949 | | if (LEFT is Add ADD && ADD.A is Constant A && RIGHT is Constant B) |
| 0 | 1950 | | { |
| 0 | 1951 | | var C = (A - B).Simplify(); |
| 0 | 1952 | | var X = ADD.B; |
| 0 | 1953 | | return C + X; |
| | 1954 | | } |
| 0 | 1955 | | } |
| 0 | 1956 | | { // Rule: [B - 'X + A'] => [C - X] where A is constant, B is constant, and C is A + B |
| 0 | 1957 | | if (RIGHT is Add ADD && ADD.B is Constant A && LEFT is Constant B) |
| 0 | 1958 | | { |
| 0 | 1959 | | var C = (A + B).Simplify(); |
| 0 | 1960 | | var X = ADD.A; |
| 0 | 1961 | | return C - X; |
| | 1962 | | } |
| 0 | 1963 | | } |
| 0 | 1964 | | { // Rule: [B - 'A + X'] => [C + X] where A is constant, B is constant, and C is B - A |
| 0 | 1965 | | if (RIGHT is Add ADD && ADD.A is Constant A && LEFT is Constant B) |
| 0 | 1966 | | { |
| 0 | 1967 | | var C = (B - A).Simplify(); |
| 0 | 1968 | | var X = ADD.B; |
| 0 | 1969 | | return C + X; |
| | 1970 | | } |
| 0 | 1971 | | } |
| | 1972 | | #endregion |
| 0 | 1973 | | return LEFT - RIGHT; |
| 0 | 1974 | | } |
| | 1975 | |
|
| | 1976 | | internal override Expression Simplify<T>(params Expression[] operands) |
| 0 | 1977 | | { |
| 0 | 1978 | | if (operands[0] is Constant<T> a && operands[1] is Constant<T> b) |
| 0 | 1979 | | { |
| 0 | 1980 | | return new Constant<T>(Subtraction(a.Value, b.Value)); |
| | 1981 | | } |
| 0 | 1982 | | return base.Simplify<T>(); |
| 0 | 1983 | | } |
| | 1984 | |
|
| | 1985 | | /// <summary>Clones this expression.</summary> |
| | 1986 | | /// <returns>A clone of this expression.</returns> |
| 0 | 1987 | | public override Expression Clone() => new Subtract(A.Clone(), B.Clone()); |
| | 1988 | |
|
| | 1989 | | /// <summary>Substitutes an expression for all occurences of a variable.</summary> |
| | 1990 | | /// <param name="variable">The variable to be substititued.</param> |
| | 1991 | | /// <param name="expression">The expression to substitute for each occurence of a variable.</param> |
| | 1992 | | /// <returns>The resulting expression of the substitution.</returns> |
| | 1993 | | public override Expression Substitute(string variable, Expression expression) => |
| 0 | 1994 | | new Subtract(A.Substitute(variable, expression), B.Substitute(variable, expression)); |
| | 1995 | |
|
| | 1996 | | /// <summary>Standard conversion to a string representation.</summary> |
| | 1997 | | /// <returns>The string represnetation of this expression.</returns> |
| | 1998 | | public override string ToString() |
| 26 | 1999 | | { |
| 26 | 2000 | | string? a = A.ToString(); |
| 26 | 2001 | | if (A is Multiply || A is Divide) |
| 2 | 2002 | | { |
| 2 | 2003 | | a = "(" + a + ")"; |
| 2 | 2004 | | } |
| 26 | 2005 | | string? b = B.ToString(); |
| 26 | 2006 | | if (B is Add || B is Subtract || A is Multiply || A is Divide) |
| 8 | 2007 | | { |
| 8 | 2008 | | b = "(" + b + ")"; |
| 8 | 2009 | | } |
| 26 | 2010 | | return a + " - " + b; |
| 26 | 2011 | | } |
| | 2012 | | } |
| | 2013 | |
|
| | 2014 | | #endregion |
| | 2015 | |
|
| | 2016 | | #endregion |
| | 2017 | |
|
| | 2018 | | #region MultiplyOrDivide + Inheriters |
| | 2019 | |
|
| | 2020 | | #region MultiplyOrDivide |
| | 2021 | |
|
| | 2022 | | /// <summary>Abstract base class for multiplication and division operations.</summary> |
| | 2023 | | public abstract class MultiplyOrDivide : Binary, Operation.IMathematical |
| | 2024 | | { |
| | 2025 | | /// <summary>Constructs a new multiplication or division operation.</summary> |
| | 2026 | | /// <param name="a">The left operand of the operation.</param> |
| | 2027 | | /// <param name="b">The right operand of the operation.</param> |
| 228 | 2028 | | public MultiplyOrDivide(Expression a, Expression b) : base(a, b) { } |
| | 2029 | | } |
| | 2030 | |
|
| | 2031 | | #endregion |
| | 2032 | |
|
| | 2033 | | #region Multiply |
| | 2034 | |
|
| | 2035 | | /// <summary>Represents a multiplication operation.</summary> |
| | 2036 | | [BinaryOperator("*", OperatorPriority.Multiplication)] |
| | 2037 | | public class Multiply : MultiplyOrDivide |
| | 2038 | | { |
| | 2039 | | /// <summary>Constructs a new multiplication operation.</summary> |
| | 2040 | | /// <param name="a">The left operand of the multiplication operation.</param> |
| | 2041 | | /// <param name="b">The right operand of the multiplication operation.</param> |
| 108 | 2042 | | public Multiply(Expression a, Expression b) : base(a, b) { } |
| | 2043 | |
|
| | 2044 | | /// <summary>Simplifies the mathematical expression.</summary> |
| | 2045 | | /// <returns>The simplified mathematical expression.</returns> |
| | 2046 | | public override Expression Simplify() |
| 6 | 2047 | | { |
| 6 | 2048 | | Expression LEFT = A.Simplify(); |
| 6 | 2049 | | Expression RIGHT = B.Simplify(); |
| | 2050 | | #region Computation |
| 6 | 2051 | | { // Rule: [A * B] => [C] where A is constant, B is constant, and C is A * B |
| 6 | 2052 | | if (LEFT is Constant A && RIGHT is Constant B) |
| 6 | 2053 | | { |
| 6 | 2054 | | return A.Simplify(this, A, B); |
| | 2055 | | } |
| 0 | 2056 | | } |
| | 2057 | | #endregion |
| | 2058 | | #region Zero Property |
| 0 | 2059 | | { // Rule: [X * 0] => [0] |
| 0 | 2060 | | if (RIGHT is Constant CONSTANT && CONSTANT.IsZero) |
| 0 | 2061 | | { |
| 0 | 2062 | | return CONSTANT; |
| | 2063 | | } |
| 0 | 2064 | | } |
| 0 | 2065 | | { // Rule: [0 * X] => [0] |
| 0 | 2066 | | if (LEFT is Constant CONSTANT && CONSTANT.IsZero) |
| 0 | 2067 | | { |
| 0 | 2068 | | return CONSTANT; |
| | 2069 | | } |
| 0 | 2070 | | } |
| | 2071 | | #endregion |
| | 2072 | | #region Identity Property |
| 0 | 2073 | | { // Rule: [X * 1] => [X] |
| 0 | 2074 | | if (RIGHT is Constant CONSTANT && CONSTANT.IsOne) |
| 0 | 2075 | | { |
| 0 | 2076 | | return LEFT; |
| | 2077 | | } |
| 0 | 2078 | | } |
| 0 | 2079 | | { // Rule: [1 * X] => [X] |
| 0 | 2080 | | if (LEFT is Constant CONSTANT && CONSTANT.IsOne) |
| 0 | 2081 | | { |
| 0 | 2082 | | return RIGHT; |
| | 2083 | | } |
| 0 | 2084 | | } |
| | 2085 | | #endregion |
| | 2086 | | #region Commutative/Associative Property |
| 0 | 2087 | | { // Rule: [(X * A) * B] => [X * C] where A is constant, B is constant, and C is A * B |
| 0 | 2088 | | if (LEFT is Multiply MULTIPLY && MULTIPLY.B is Constant A && RIGHT is Constant B) |
| 0 | 2089 | | { |
| 0 | 2090 | | var C = (A * B).Simplify(); |
| 0 | 2091 | | var X = MULTIPLY.A; |
| 0 | 2092 | | return X * C; |
| | 2093 | | } |
| 0 | 2094 | | } |
| 0 | 2095 | | { // Rule: [(A * X) * B] => [X * C] where A is constant, B is constant, and C is A * B |
| 0 | 2096 | | if (LEFT is Multiply MULTIPLY && MULTIPLY.A is Constant A && RIGHT is Constant B) |
| 0 | 2097 | | { |
| 0 | 2098 | | var C = (A * B).Simplify(); |
| 0 | 2099 | | var X = MULTIPLY.B; |
| 0 | 2100 | | return X * C; |
| | 2101 | | } |
| 0 | 2102 | | } |
| 0 | 2103 | | { // Rule: [B * (X * A)] => [X * C] where A is constant, B is constant, and C is A * B |
| 0 | 2104 | | if (RIGHT is Multiply MULTIPLY && MULTIPLY.B is Constant A && LEFT is Constant B) |
| 0 | 2105 | | { |
| 0 | 2106 | | var C = (A * B).Simplify(); |
| 0 | 2107 | | var X = MULTIPLY.A; |
| 0 | 2108 | | return X * C; |
| | 2109 | | } |
| 0 | 2110 | | } |
| 0 | 2111 | | { // Rule: [B * (A * X)] => [X * C] where A is constant, B is constant, and C is A * B |
| 0 | 2112 | | if (RIGHT is Multiply MULTIPLY && MULTIPLY.A is Constant A && LEFT is Constant B) |
| 0 | 2113 | | { |
| 0 | 2114 | | var C = (A * B).Simplify(); |
| 0 | 2115 | | var X = MULTIPLY.B; |
| 0 | 2116 | | return X * C; |
| | 2117 | | } |
| 0 | 2118 | | } |
| 0 | 2119 | | { // Rule: [(X / A) * B] => [X * C] where A is constant, B is constant, and C is B / A |
| 0 | 2120 | | if (LEFT is Divide DIVIDE && DIVIDE.B is Constant A && RIGHT is Constant B) |
| 0 | 2121 | | { |
| 0 | 2122 | | var C = (B / A).Simplify(); |
| 0 | 2123 | | var X = DIVIDE.A; |
| 0 | 2124 | | return X * C; |
| | 2125 | | } |
| 0 | 2126 | | } |
| 0 | 2127 | | { // Rule: [(A / X) * B] => [C / X] where A is constant, B is constant, and C is A * B |
| 0 | 2128 | | if (LEFT is Divide DIVIDE && DIVIDE.A is Constant A && RIGHT is Constant B) |
| 0 | 2129 | | { |
| 0 | 2130 | | var C = (A * B).Simplify(); |
| 0 | 2131 | | var X = DIVIDE.B; |
| 0 | 2132 | | return C / X; |
| | 2133 | | } |
| 0 | 2134 | | } |
| 0 | 2135 | | { // Rule: [B * (X / A)] => [X * C] where A is constant, B is constant, and C is B / A |
| 0 | 2136 | | if (RIGHT is Divide DIVIDE && DIVIDE.B is Constant A && LEFT is Constant B) |
| 0 | 2137 | | { |
| 0 | 2138 | | var C = (B / A).Simplify(); |
| 0 | 2139 | | var X = DIVIDE.A; |
| 0 | 2140 | | return X * C; |
| | 2141 | | } |
| 0 | 2142 | | } |
| 0 | 2143 | | { // Rule: [B * (A / X)] => [C / X] where A is constant, B is constant, and C is A * B |
| 0 | 2144 | | if (RIGHT is Divide DIVIDE && DIVIDE.A is Constant A && LEFT is Constant B) |
| 0 | 2145 | | { |
| 0 | 2146 | | var C = (A * B).Simplify(); |
| 0 | 2147 | | var X = DIVIDE.B; |
| 0 | 2148 | | return C / X; |
| | 2149 | | } |
| 0 | 2150 | | } |
| | 2151 | | #endregion |
| | 2152 | | #region Distributive Property |
| | 2153 | | // { // Rule: [X * (A +/- B)] => [X * A + X * B] where X is Variable |
| | 2154 | | // if ((LEFT is Variable VARIABLE && RIGHT is AddOrSubtract ADDORSUBTRACT)) |
| | 2155 | | // { |
| | 2156 | | // // This might not be necessary |
| | 2157 | | // } |
| | 2158 | | // } |
| | 2159 | | // { // Rule: [(A +/- B) * X] => [X * A + X * B] where X is Variable |
| | 2160 | | // if ((RIGHT is Variable VARIABLE && LEFT is AddOrSubtract ADDORSUBTRACT)) |
| | 2161 | | // { |
| | 2162 | | // // This might not be necessary |
| | 2163 | | // } |
| | 2164 | | // } |
| | 2165 | | #endregion |
| | 2166 | | #region Duplicate Variable Multiplications |
| 0 | 2167 | | { // Rule: [X * X] => [X ^ 2] where X is Variable |
| 0 | 2168 | | if (LEFT is Variable X1 && RIGHT is Variable X2 && X1.Name == X2.Name) |
| 0 | 2169 | | { |
| 0 | 2170 | | return X1 ^ new Two(); |
| | 2171 | | } |
| 0 | 2172 | | } |
| | 2173 | | #endregion |
| | 2174 | | #region Multiplication With Powered Variables |
| 0 | 2175 | | { // Rule: [(V ^ A) * (V ^ B)] => [V ^ C] where A is constant, B is constant, V is a variable, and C is A + B |
| 0 | 2176 | | if (LEFT is Power POWER1 && RIGHT is Power POWER2 && |
| 0 | 2177 | | POWER1.A is Variable V1 && POWER2.A is Variable V2 && V1.Name == V2.Name && |
| 0 | 2178 | | POWER1.B is Constant A && POWER2.B is Constant B) |
| 0 | 2179 | | { |
| 0 | 2180 | | var C = (A + B).Simplify(); |
| 0 | 2181 | | return V1 ^ C; |
| | 2182 | | } |
| 0 | 2183 | | } |
| | 2184 | | #endregion |
| 0 | 2185 | | return LEFT * RIGHT; |
| 6 | 2186 | | } |
| | 2187 | |
|
| | 2188 | | internal override Expression Simplify<T>(params Expression[] operands) |
| 6 | 2189 | | { |
| 6 | 2190 | | if (operands[0] is Constant<T> a && operands[1] is Constant<T> b) |
| 6 | 2191 | | { |
| 6 | 2192 | | return new Constant<T>(Multiplication(a.Value, b.Value)); |
| | 2193 | | } |
| 0 | 2194 | | return base.Simplify<T>(); |
| 6 | 2195 | | } |
| | 2196 | |
|
| | 2197 | | /// <summary>Clones this expression.</summary> |
| | 2198 | | /// <returns>A clone of this expression.</returns> |
| 0 | 2199 | | public override Expression Clone() => new Multiply(A.Clone(), B.Clone()); |
| | 2200 | |
|
| | 2201 | | /// <summary>Substitutes an expression for all occurences of a variable.</summary> |
| | 2202 | | /// <param name="variable">The variable to be substititued.</param> |
| | 2203 | | /// <param name="expression">The expression to substitute for each occurence of a variable.</param> |
| | 2204 | | /// <returns>The resulting expression of the substitution.</returns> |
| | 2205 | | public override Expression Substitute(string variable, Expression expression) => |
| 0 | 2206 | | new Multiply(A.Substitute(variable, expression), B.Substitute(variable, expression)); |
| | 2207 | |
|
| | 2208 | | /// <summary>Standard conversion to a string representation.</summary> |
| | 2209 | | /// <returns>The string represnetation of this expression.</returns> |
| | 2210 | | public override string ToString() |
| 28 | 2211 | | { |
| 28 | 2212 | | string? a = A.ToString(); |
| 28 | 2213 | | string? b = B.ToString(); |
| 28 | 2214 | | if (B is Multiply || B is Divide) |
| 6 | 2215 | | { |
| 6 | 2216 | | b = "(" + b + ")"; |
| 6 | 2217 | | } |
| 22 | 2218 | | else if (A is Constant a_const && a_const.IsKnownConstant && B is Constant) |
| 0 | 2219 | | { |
| 0 | 2220 | | return b + a; |
| | 2221 | | } |
| 22 | 2222 | | else if (A is Constant && B is Constant b_const && b_const.IsKnownConstant) |
| 0 | 2223 | | { |
| 0 | 2224 | | return a + b; |
| | 2225 | | } |
| 28 | 2226 | | return a + " * " + b; |
| 28 | 2227 | | } |
| | 2228 | | } |
| | 2229 | |
|
| | 2230 | | #endregion |
| | 2231 | |
|
| | 2232 | | #region Divide |
| | 2233 | |
|
| | 2234 | | /// <summary>Represents a division operation.</summary> |
| | 2235 | | [BinaryOperator("/", OperatorPriority.Division)] |
| | 2236 | | public class Divide : MultiplyOrDivide |
| | 2237 | | { |
| | 2238 | | /// <summary>Constructs a new division operation.</summary> |
| | 2239 | | /// <param name="a">The left operand of the division operation.</param> |
| | 2240 | | /// <param name="b">The right operand of the division operation.</param> |
| 120 | 2241 | | public Divide(Expression a, Expression b) : base(a, b) { } |
| | 2242 | |
|
| | 2243 | | /// <summary>Simplifies the mathematical expression.</summary> |
| | 2244 | | /// <returns>The simplified mathematical expression.</returns> |
| | 2245 | | public override Expression Simplify() |
| 18 | 2246 | | { |
| 18 | 2247 | | Expression LEFT = A.Simplify(); |
| 18 | 2248 | | Expression RIGHT = B.Simplify(); |
| | 2249 | | #region Error Handling |
| 18 | 2250 | | { // Rule: [X / 0] => Error |
| 18 | 2251 | | if (RIGHT is Constant CONSTANT && CONSTANT.IsZero) |
| 0 | 2252 | | { |
| 0 | 2253 | | throw new DivideByZeroException(); |
| | 2254 | | } |
| 18 | 2255 | | } |
| | 2256 | | #endregion |
| | 2257 | | #region Computation |
| 18 | 2258 | | { // Rule: [A / B] => [C] where A is constant, B is constant, and C is A / B |
| 18 | 2259 | | if (LEFT is Constant A && RIGHT is Constant B) |
| 18 | 2260 | | { |
| 18 | 2261 | | return A.Simplify(this, A, B); |
| | 2262 | | } |
| 0 | 2263 | | } |
| | 2264 | | #endregion |
| | 2265 | | #region Zero Property |
| 0 | 2266 | | { // Rule: [0 / X] => [0] |
| 0 | 2267 | | if (LEFT is Constant CONSTANT && CONSTANT.IsZero) |
| 0 | 2268 | | { |
| 0 | 2269 | | return CONSTANT; |
| | 2270 | | } |
| 0 | 2271 | | } |
| | 2272 | | #endregion |
| | 2273 | | #region Identity Property |
| 0 | 2274 | | { // Rule: [X / 1] => [X] |
| 0 | 2275 | | if (RIGHT is Constant CONSTANT && CONSTANT.IsOne) |
| 0 | 2276 | | { |
| 0 | 2277 | | return LEFT; |
| | 2278 | | } |
| 0 | 2279 | | } |
| | 2280 | | #endregion |
| | 2281 | | #region Commutative/Associative Property |
| 0 | 2282 | | { // Rule: [(X / A) / B] => [X / C] where A is constant, B is constant, and C is A * B |
| 0 | 2283 | | if (LEFT is Divide DIVIDE && DIVIDE.B is Constant A && RIGHT is Constant B) |
| 0 | 2284 | | { |
| 0 | 2285 | | var C = (A * B).Simplify(); |
| 0 | 2286 | | var X = DIVIDE.A; |
| 0 | 2287 | | return X / C; |
| | 2288 | | } |
| 0 | 2289 | | } |
| 0 | 2290 | | { // Rule: [(A / X) / B] => [C / X] where A is constant, B is constant, and C is A / B |
| 0 | 2291 | | if (LEFT is Divide DIVIDE && DIVIDE.A is Constant A && RIGHT is Constant B) |
| 0 | 2292 | | { |
| 0 | 2293 | | var C = (A / B).Simplify(); |
| 0 | 2294 | | var X = DIVIDE.B; |
| 0 | 2295 | | return C / X; |
| | 2296 | | } |
| 0 | 2297 | | } |
| 0 | 2298 | | { // Rule: [B / (X / A)] => [C / X] where A is constant, B is constant, and C is B / A |
| 0 | 2299 | | if (RIGHT is Divide DIVIDE && DIVIDE.B is Constant A && LEFT is Constant B) |
| 0 | 2300 | | { |
| 0 | 2301 | | var C = (B / A).Simplify(); |
| 0 | 2302 | | var X = DIVIDE.A; |
| 0 | 2303 | | return C / X; |
| | 2304 | | } |
| 0 | 2305 | | } |
| 0 | 2306 | | { // Rule: [B / (A / X)] => [C / X] where A is constant, B is constant, and C is B / A |
| 0 | 2307 | | if (RIGHT is Divide DIVIDE && DIVIDE.A is Constant A && LEFT is Constant B) |
| 0 | 2308 | | { |
| 0 | 2309 | | var C = (B / A).Simplify(); |
| 0 | 2310 | | var X = DIVIDE.B; |
| 0 | 2311 | | return C / X; |
| | 2312 | | } |
| 0 | 2313 | | } |
| 0 | 2314 | | { // Rule: [(X * A) / B] => [X * C] where A is constant, B is constant, and C is A / B |
| 0 | 2315 | | if (LEFT is Multiply MULTIPLY && MULTIPLY.B is Constant A && RIGHT is Constant B) |
| 0 | 2316 | | { |
| 0 | 2317 | | var C = (A / B).Simplify(); |
| 0 | 2318 | | var X = MULTIPLY.A; |
| 0 | 2319 | | return X * C; |
| | 2320 | | } |
| 0 | 2321 | | } |
| 0 | 2322 | | { // Rule: [(A * X) / B] => [X * C] where A is constant, B is constant, and C is A / B |
| 0 | 2323 | | if (LEFT is Multiply MULTIPLY && MULTIPLY.A is Constant A && RIGHT is Constant B) |
| 0 | 2324 | | { |
| 0 | 2325 | | var C = (A / B).Simplify(); |
| 0 | 2326 | | var X = MULTIPLY.B; |
| 0 | 2327 | | return X * C; |
| | 2328 | | } |
| 0 | 2329 | | } |
| 0 | 2330 | | { // Rule: [B / (X * A)] => [C / X] where A is constant, B is constant, and C is A * B |
| 0 | 2331 | | if (RIGHT is Multiply MULTIPLY && MULTIPLY.B is Constant A && LEFT is Constant B) |
| 0 | 2332 | | { |
| 0 | 2333 | | var C = (A * B).Simplify(); |
| 0 | 2334 | | var X = MULTIPLY.A; |
| 0 | 2335 | | return C / X; |
| | 2336 | | } |
| 0 | 2337 | | } |
| 0 | 2338 | | { // Rule: [B / (A * X)] => [X * C] where A is constant, B is constant, and C is B / A |
| 0 | 2339 | | if (RIGHT is Multiply MULTIPLY && MULTIPLY.A is Constant A && LEFT is Constant B) |
| 0 | 2340 | | { |
| 0 | 2341 | | var C = (B / A).Simplify(); |
| 0 | 2342 | | var X = MULTIPLY.B; |
| 0 | 2343 | | return X * C; |
| | 2344 | | } |
| 0 | 2345 | | } |
| | 2346 | | #endregion |
| | 2347 | | #region Distributive Property |
| | 2348 | | // { // Rule: [X / (A +/- B)] => [X / A + X / B] where where A is constant, B is constant, and X is Variable |
| | 2349 | | // if ((LEFT is Variable VARIABLE && RIGHT is AddOrSubtract ADDORSUBTRACT)) |
| | 2350 | | // { |
| | 2351 | | // // This might not be necessary |
| | 2352 | | // } |
| | 2353 | | // } |
| | 2354 | | // { // Rule: [(A +/- B) / X] => [(A / X) + (B / X)] where where A is constant, B is constant, and X is Variable |
| | 2355 | | // if ((RIGHT is Variable VARIABLE && LEFT is AddOrSubtract ADDORSUBTRACT)) |
| | 2356 | | // { |
| | 2357 | | // // This might not be necessary |
| | 2358 | | // } |
| | 2359 | | // } |
| | 2360 | | #endregion |
| | 2361 | | #region Division With Powered Variables |
| 0 | 2362 | | { // Rule: [(V ^ A) / (V ^ B)] => [V ^ C] where A is constant, B is constant, V is a variable, and C is A - B |
| 0 | 2363 | | if (LEFT is Power POWER1 && RIGHT is Power POWER2 && |
| 0 | 2364 | | POWER1.A is Variable V1 && POWER2.A is Variable V2 && V1.Name == V2.Name && |
| 0 | 2365 | | POWER1.B is Constant A && POWER2.B is Constant B) |
| 0 | 2366 | | { |
| 0 | 2367 | | var C = (A - B).Simplify(); |
| 0 | 2368 | | return V1 ^ C; |
| | 2369 | | } |
| 0 | 2370 | | } |
| | 2371 | | #endregion |
| 0 | 2372 | | return LEFT / RIGHT; |
| 18 | 2373 | | } |
| | 2374 | |
|
| | 2375 | | internal override Expression Simplify<T>(params Expression[] operands) |
| 18 | 2376 | | { |
| 18 | 2377 | | if (operands[0] is Constant<T> a && operands[1] is Constant<T> b) |
| 18 | 2378 | | { |
| 18 | 2379 | | return new Constant<T>(Division(a.Value, b.Value)); |
| | 2380 | | } |
| 0 | 2381 | | return base.Simplify<T>(); |
| 18 | 2382 | | } |
| | 2383 | |
|
| | 2384 | | /// <summary>Clones this expression.</summary> |
| | 2385 | | /// <returns>A clone of this expression.</returns> |
| 0 | 2386 | | public override Expression Clone() => new Divide(A.Clone(), B.Clone()); |
| | 2387 | |
|
| | 2388 | | /// <summary>Substitutes an expression for all occurences of a variable.</summary> |
| | 2389 | | /// <param name="variable">The variable to be substititued.</param> |
| | 2390 | | /// <param name="expression">The expression to substitute for each occurence of a variable.</param> |
| | 2391 | | /// <returns>The resulting expression of the substitution.</returns> |
| | 2392 | | public override Expression Substitute(string variable, Expression expression) => |
| 0 | 2393 | | new Divide(A.Substitute(variable, expression), B.Substitute(variable, expression)); |
| | 2394 | |
|
| | 2395 | | /// <summary>Standard conversion to a string representation.</summary> |
| | 2396 | | /// <returns>The string represnetation of this expression.</returns> |
| | 2397 | | public override string ToString() |
| 20 | 2398 | | { |
| 20 | 2399 | | string? a = A.ToString(); |
| 20 | 2400 | | string? b = B.ToString(); |
| 20 | 2401 | | if (B is Multiply || B is Divide) |
| 6 | 2402 | | { |
| 6 | 2403 | | b = "(" + b + ")"; |
| 6 | 2404 | | } |
| 20 | 2405 | | return a + " / " + b; |
| 20 | 2406 | | } |
| | 2407 | | } |
| | 2408 | |
|
| | 2409 | | #endregion |
| | 2410 | |
|
| | 2411 | | #endregion |
| | 2412 | |
|
| | 2413 | | #region Power |
| | 2414 | |
|
| | 2415 | | /// <summary>Represents a power operation.</summary> |
| | 2416 | | [BinaryOperator("^", OperatorPriority.Exponents)] |
| | 2417 | | public class Power : Binary, Operation.IMathematical |
| | 2418 | | { |
| | 2419 | | /// <summary>Constructs a new power operation.</summary> |
| | 2420 | | /// <param name="a">The left operand of the power operation.</param> |
| | 2421 | | /// <param name="b">The right operand of the power operation.</param> |
| 0 | 2422 | | public Power(Expression a, Expression b) : base(a, b) { } |
| | 2423 | |
|
| | 2424 | | /// <summary>Simplifies the mathematical expression.</summary> |
| | 2425 | | /// <returns>The simplified mathematical expression.</returns> |
| | 2426 | | public override Expression Simplify() |
| 0 | 2427 | | { |
| 0 | 2428 | | Expression LEFT = A.Simplify(); |
| 0 | 2429 | | Expression RIGHT = B.Simplify(); |
| | 2430 | | #region Computation |
| 0 | 2431 | | { // Rule: [A ^ B] => [C] where A is constant, B is constant, and C is A ^ B |
| 0 | 2432 | | if (LEFT is Constant A && RIGHT is Constant B) |
| 0 | 2433 | | { |
| 0 | 2434 | | return A.Simplify(this, A, B); |
| | 2435 | | } |
| 0 | 2436 | | } |
| | 2437 | | #endregion |
| | 2438 | | #region Zero Base |
| 0 | 2439 | | { // Rule: [0 ^ X] => [0] |
| 0 | 2440 | | if (LEFT is Constant CONSTANT && CONSTANT.IsZero) |
| 0 | 2441 | | { |
| 0 | 2442 | | return CONSTANT; |
| | 2443 | | } |
| 0 | 2444 | | } |
| | 2445 | | #endregion |
| | 2446 | | #region One Power |
| 0 | 2447 | | { // Rule: [X ^ 1] => [X] |
| 0 | 2448 | | if (RIGHT is Constant CONSTANT && CONSTANT.IsOne) |
| 0 | 2449 | | { |
| 0 | 2450 | | return LEFT; |
| | 2451 | | } |
| 0 | 2452 | | } |
| | 2453 | | #endregion |
| | 2454 | | #region Zero Power |
| 0 | 2455 | | { // Rule: [X ^ 0] => [1] |
| 0 | 2456 | | if (RIGHT is Constant CONSTANT && CONSTANT.IsZero) |
| 0 | 2457 | | { |
| 0 | 2458 | | return new One(); |
| | 2459 | | } |
| 0 | 2460 | | } |
| | 2461 | | #endregion |
| 0 | 2462 | | return LEFT ^ RIGHT; |
| 0 | 2463 | | } |
| | 2464 | |
|
| | 2465 | | internal override Expression Simplify<T>(params Expression[] operands) |
| 0 | 2466 | | { |
| 0 | 2467 | | if (operands[0] is Constant<T> a && operands[1] is Constant<T> b) |
| 0 | 2468 | | { |
| 0 | 2469 | | return new Constant<T>(Power(a.Value, b.Value)); |
| | 2470 | | } |
| 0 | 2471 | | return base.Simplify<T>(); |
| 0 | 2472 | | } |
| | 2473 | |
|
| | 2474 | | /// <summary>Clones this expression.</summary> |
| | 2475 | | /// <returns>A clone of this expression.</returns> |
| 0 | 2476 | | public override Expression Clone() => new Power(A.Clone(), B.Clone()); |
| | 2477 | |
|
| | 2478 | | /// <summary>Substitutes an expression for all occurences of a variable.</summary> |
| | 2479 | | /// <param name="variable">The variable to be substititued.</param> |
| | 2480 | | /// <param name="expression">The expression to substitute for each occurence of a variable.</param> |
| | 2481 | | /// <returns>The resulting expression of the substitution.</returns> |
| | 2482 | | public override Expression Substitute(string variable, Expression expression) => |
| 0 | 2483 | | new Power(A.Substitute(variable, expression), B.Substitute(variable, expression)); |
| | 2484 | |
|
| | 2485 | | /// <summary>Standard conversion to a string representation.</summary> |
| | 2486 | | /// <returns>The string represnetation of this expression.</returns> |
| | 2487 | | public override string ToString() |
| 0 | 2488 | | { |
| | 2489 | | static bool RequiresParentheses(string expressionString) |
| 0 | 2490 | | { |
| 0 | 2491 | | foreach (char c in expressionString) |
| 0 | 2492 | | { |
| 0 | 2493 | | if (!char.IsDigit(c) && c != '.') |
| 0 | 2494 | | { |
| 0 | 2495 | | return true; |
| | 2496 | | } |
| 0 | 2497 | | } |
| 0 | 2498 | | return false; |
| 0 | 2499 | | } |
| | 2500 | |
|
| 0 | 2501 | | string? a = A.ToString(); |
| 0 | 2502 | | string? b = B.ToString(); |
| 0 | 2503 | | if (a is not null && RequiresParentheses(a)) |
| 0 | 2504 | | { |
| 0 | 2505 | | a = "(" + a + ")"; |
| 0 | 2506 | | } |
| 0 | 2507 | | if (b is not null && RequiresParentheses(b)) |
| 0 | 2508 | | { |
| 0 | 2509 | | b = "(" + b + ")"; |
| 0 | 2510 | | } |
| 0 | 2511 | | return a + " ^ " + b; |
| 0 | 2512 | | } |
| | 2513 | | } |
| | 2514 | |
|
| | 2515 | | #endregion |
| | 2516 | |
|
| | 2517 | | #region Root |
| | 2518 | |
|
| | 2519 | | /// <summary>Represents a root operation.</summary> |
| | 2520 | | public class Root : Binary, Operation.IMathematical |
| | 2521 | | { |
| | 2522 | | /// <summary>Constructs a new root operation.</summary> |
| | 2523 | | /// <param name="a">The base of the root operation.</param> |
| | 2524 | | /// <param name="b">The root (inverted exponent) value of the operation.</param> |
| 0 | 2525 | | public Root(Expression a, Expression b) : base(a, b) { } |
| | 2526 | |
|
| | 2527 | | /// <summary>Simplifies the mathematical expression.</summary> |
| | 2528 | | /// <returns>The simplified mathematical expression.</returns> |
| | 2529 | | public override Expression Simplify() |
| 0 | 2530 | | { |
| 0 | 2531 | | Expression LEFT = A.Simplify(); |
| 0 | 2532 | | Expression RIGHT = B.Simplify(); |
| | 2533 | | #region Computation |
| 0 | 2534 | | { // Rule: [A ^ (1 / B)] => [C] where A is constant, B is constant, and C is A ^ (1 / B) |
| 0 | 2535 | | if (LEFT is Constant A && RIGHT is Constant B) |
| 0 | 2536 | | { |
| 0 | 2537 | | return A.Simplify(this, A, B); |
| | 2538 | | } |
| 0 | 2539 | | } |
| | 2540 | | #endregion |
| 0 | 2541 | | return new Root(LEFT, RIGHT); |
| 0 | 2542 | | } |
| | 2543 | |
|
| | 2544 | | internal override Expression Simplify<T>(params Expression[] operands) |
| 0 | 2545 | | { |
| 0 | 2546 | | if (operands[0] is Constant<T> a && operands[1] is Constant<T> b) |
| 0 | 2547 | | { |
| 0 | 2548 | | return new Constant<T>(Root(a.Value, b.Value)); |
| | 2549 | | } |
| 0 | 2550 | | return base.Simplify<T>(); |
| 0 | 2551 | | } |
| | 2552 | |
|
| | 2553 | | /// <summary>Clones this expression.</summary> |
| | 2554 | | /// <returns>A clone of this expression.</returns> |
| 0 | 2555 | | public override Expression Clone() => new Root(A.Clone(), B.Clone()); |
| | 2556 | |
|
| | 2557 | | /// <summary>Substitutes an expression for all occurences of a variable.</summary> |
| | 2558 | | /// <param name="variable">The variable to be substititued.</param> |
| | 2559 | | /// <param name="expression">The expression to substitute for each occurence of a variable.</param> |
| | 2560 | | /// <returns>The resulting expression of the substitution.</returns> |
| | 2561 | | public override Expression Substitute(string variable, Expression expression) => |
| 0 | 2562 | | new Root(A.Substitute(variable, expression), B.Substitute(variable, expression)); |
| | 2563 | |
|
| | 2564 | | /// <summary>Standard conversion to a string representation.</summary> |
| | 2565 | | /// <returns>The string represnetation of this expression.</returns> |
| 0 | 2566 | | public override string ToString() => A + " ^ (1 / " + B + ")"; |
| | 2567 | | } |
| | 2568 | |
|
| | 2569 | | #endregion |
| | 2570 | |
|
| | 2571 | | #region Equal |
| | 2572 | |
|
| | 2573 | | /// <summary>Represents an equality operation between two expressions.</summary> |
| | 2574 | | [BinaryOperator("=", OperatorPriority.Logical)] |
| | 2575 | | public class Equal : Binary, Operation.ILogical |
| | 2576 | | { |
| | 2577 | | /// <summary>Constructs a new equality operation between two expressions.</summary> |
| | 2578 | | /// <param name="a">The left expression of the equality.</param> |
| | 2579 | | /// <param name="b">The right expression of the equality.</param> |
| 0 | 2580 | | public Equal(Expression a, Expression b) : base(a, b) { } |
| | 2581 | |
|
| | 2582 | | /// <summary>Simplifies the mathematical expression.</summary> |
| | 2583 | | /// <returns>The simplified mathematical expression.</returns> |
| | 2584 | | public override Expression Simplify() |
| 0 | 2585 | | { |
| 0 | 2586 | | Expression LEFT = A.Simplify(); |
| 0 | 2587 | | Expression RIGHT = B.Simplify(); |
| | 2588 | | #region Computation |
| 0 | 2589 | | { // Rule: [A == B] => [C] where A is constant, B is constant, and C is A == B |
| 0 | 2590 | | if (LEFT is Constant A && RIGHT is Constant B) |
| 0 | 2591 | | { |
| 0 | 2592 | | return A.Simplify(this, A, B); |
| | 2593 | | } |
| 0 | 2594 | | } |
| | 2595 | | #endregion |
| 0 | 2596 | | return LEFT == RIGHT; |
| 0 | 2597 | | } |
| | 2598 | |
|
| | 2599 | | internal override Expression Simplify<T>(params Expression[] operands) |
| 0 | 2600 | | { |
| 0 | 2601 | | if (operands[0] is Constant<T> a && operands[1] is Constant<T> b) |
| 0 | 2602 | | { |
| 0 | 2603 | | return new Constant<bool>(Equate(a.Value, b.Value)); |
| | 2604 | | } |
| 0 | 2605 | | return base.Simplify<T>(); |
| 0 | 2606 | | } |
| | 2607 | |
|
| | 2608 | | /// <summary>Clones this expression.</summary> |
| | 2609 | | /// <returns>A clone of this expression.</returns> |
| 0 | 2610 | | public override Expression Clone() => new Equal(A.Clone(), B.Clone()); |
| | 2611 | |
|
| | 2612 | | /// <summary>Substitutes an expression for all occurences of a variable.</summary> |
| | 2613 | | /// <param name="variable">The variable to be substititued.</param> |
| | 2614 | | /// <param name="expression">The expression to substitute for each occurence of a variable.</param> |
| | 2615 | | /// <returns>The resulting expression of the substitution.</returns> |
| | 2616 | | public override Expression Substitute(string variable, Expression expression) => |
| 0 | 2617 | | new Equal(A.Substitute(variable, expression), B.Substitute(variable, expression)); |
| | 2618 | |
|
| | 2619 | | /// <summary>Standard conversion to a string representation.</summary> |
| | 2620 | | /// <returns>The string represnetation of this expression.</returns> |
| 0 | 2621 | | public override string ToString() => A + " = " + B; |
| | 2622 | | } |
| | 2623 | |
|
| | 2624 | | #endregion |
| | 2625 | |
|
| | 2626 | | #region NotEqual |
| | 2627 | |
|
| | 2628 | | /// <summary>Represents an equality operation between two expressions.</summary> |
| | 2629 | | [BinaryOperator("≠", OperatorPriority.Logical)] |
| | 2630 | | public class NotEqual : Binary, Operation.ILogical |
| | 2631 | | { |
| | 2632 | | /// <summary>Constructs a new inequality operation between two expressions.</summary> |
| | 2633 | | /// <param name="a">The left expression of the inequality.</param> |
| | 2634 | | /// <param name="b">The right expression of the inequality.</param> |
| 0 | 2635 | | public NotEqual(Expression a, Expression b) : base(a, b) { } |
| | 2636 | |
|
| | 2637 | | /// <summary>Simplifies the mathematical expression.</summary> |
| | 2638 | | /// <returns>The simplified mathematical expression.</returns> |
| | 2639 | | public override Expression Simplify() |
| 0 | 2640 | | { |
| 0 | 2641 | | Expression LEFT = A.Simplify(); |
| 0 | 2642 | | Expression RIGHT = B.Simplify(); |
| | 2643 | | #region Computation |
| 0 | 2644 | | { // Rule: [A == B] => [C] where A is constant, B is constant, and C is A != B |
| 0 | 2645 | | if (LEFT is Constant A && RIGHT is Constant B) |
| 0 | 2646 | | { |
| 0 | 2647 | | return A.Simplify(this, A, B); |
| | 2648 | | } |
| 0 | 2649 | | } |
| | 2650 | | #endregion |
| 0 | 2651 | | return new NotEqual(LEFT, RIGHT); |
| 0 | 2652 | | } |
| | 2653 | |
|
| | 2654 | | internal override Expression Simplify<T>(params Expression[] operands) |
| 0 | 2655 | | { |
| 0 | 2656 | | if (operands[0] is Constant<T> a && operands[1] is Constant<T> b) |
| 0 | 2657 | | { |
| 0 | 2658 | | return new Constant<bool>(Inequate(a.Value, b.Value)); |
| | 2659 | | } |
| 0 | 2660 | | return base.Simplify<T>(); |
| 0 | 2661 | | } |
| | 2662 | |
|
| | 2663 | | /// <summary>Clones this expression.</summary> |
| | 2664 | | /// <returns>A clone of this expression.</returns> |
| 0 | 2665 | | public override Expression Clone() => new NotEqual(A.Clone(), B.Clone()); |
| | 2666 | |
|
| | 2667 | | /// <summary>Substitutes an expression for all occurences of a variable.</summary> |
| | 2668 | | /// <param name="variable">The variable to be substititued.</param> |
| | 2669 | | /// <param name="expression">The expression to substitute for each occurence of a variable.</param> |
| | 2670 | | /// <returns>The resulting expression of the substitution.</returns> |
| | 2671 | | public override Expression Substitute(string variable, Expression expression) => |
| 0 | 2672 | | new NotEqual(A.Substitute(variable, expression), B.Substitute(variable, expression)); |
| | 2673 | |
|
| | 2674 | | /// <summary>Standard conversion to a string representation.</summary> |
| | 2675 | | /// <returns>The string represnetation of this expression.</returns> |
| 0 | 2676 | | public override string ToString() => A + " ≠" + B; |
| | 2677 | | } |
| | 2678 | |
|
| | 2679 | | #endregion |
| | 2680 | |
|
| | 2681 | | #region LessThan |
| | 2682 | |
|
| | 2683 | | /// <summary>Represents a less than operation.</summary> |
| | 2684 | | [BinaryOperator("<", OperatorPriority.Logical)] |
| | 2685 | | public class LessThan : Binary, Operation.ILogical |
| | 2686 | | { |
| | 2687 | | /// <summary>Constructs a new less than operation.</summary> |
| | 2688 | | /// <param name="a">The left expression of the less than operation.</param> |
| | 2689 | | /// <param name="b">The right expression of the less than operation.</param> |
| 0 | 2690 | | public LessThan(Expression a, Expression b) : base(a, b) { } |
| | 2691 | |
|
| | 2692 | | /// <summary>Simplifies the mathematical expression.</summary> |
| | 2693 | | /// <returns>The simplified mathematical expression.</returns> |
| | 2694 | | public override Expression Simplify() |
| 0 | 2695 | | { |
| 0 | 2696 | | Expression LEFT = A.Simplify(); |
| 0 | 2697 | | Expression RIGHT = B.Simplify(); |
| | 2698 | | #region Computation |
| 0 | 2699 | | { // Rule: [A == B] => [C] where A is constant, B is constant, and C is A < B |
| 0 | 2700 | | if (LEFT is Constant A && RIGHT is Constant B) |
| 0 | 2701 | | { |
| 0 | 2702 | | return A.Simplify(this, A, B); |
| | 2703 | | } |
| 0 | 2704 | | } |
| | 2705 | | #endregion |
| 0 | 2706 | | return new LessThan(LEFT, RIGHT); |
| 0 | 2707 | | } |
| | 2708 | |
|
| | 2709 | | internal override Expression Simplify<T>(params Expression[] operands) |
| 0 | 2710 | | { |
| 0 | 2711 | | if (operands[0] is Constant<T> a && operands[1] is Constant<T> b) |
| 0 | 2712 | | { |
| 0 | 2713 | | return new Constant<bool>(LessThan(a.Value, b.Value)); |
| | 2714 | | } |
| 0 | 2715 | | return base.Simplify<T>(); |
| 0 | 2716 | | } |
| | 2717 | |
|
| | 2718 | | /// <summary>Clones this expression.</summary> |
| | 2719 | | /// <returns>A clone of this expression.</returns> |
| 0 | 2720 | | public override Expression Clone() => new LessThan(A.Clone(), B.Clone()); |
| | 2721 | |
|
| | 2722 | | /// <summary>Substitutes an expression for all occurences of a variable.</summary> |
| | 2723 | | /// <param name="variable">The variable to be substititued.</param> |
| | 2724 | | /// <param name="expression">The expression to substitute for each occurence of a variable.</param> |
| | 2725 | | /// <returns>The resulting expression of the substitution.</returns> |
| | 2726 | | public override Expression Substitute(string variable, Expression expression) => |
| 0 | 2727 | | new LessThan(A.Substitute(variable, expression), B.Substitute(variable, expression)); |
| | 2728 | |
|
| | 2729 | | /// <summary>Standard conversion to a string representation.</summary> |
| | 2730 | | /// <returns>The string represnetation of this expression.</returns> |
| 0 | 2731 | | public override string ToString() => A + " < " + B; |
| | 2732 | | } |
| | 2733 | |
|
| | 2734 | | #endregion |
| | 2735 | |
|
| | 2736 | | #region GreaterThan |
| | 2737 | |
|
| | 2738 | | /// <summary>Represents a greater than operation.</summary> |
| | 2739 | | [BinaryOperator(">", OperatorPriority.Logical)] |
| | 2740 | | public class GreaterThan : Binary, Operation.ILogical |
| | 2741 | | { |
| | 2742 | | /// <summary>Constructs a new greater than operation.</summary> |
| | 2743 | | /// <param name="a">The left expression of the greater than operation.</param> |
| | 2744 | | /// <param name="b">The right expression of the greater than operation.</param> |
| 0 | 2745 | | public GreaterThan(Expression a, Expression b) : base(a, b) { } |
| | 2746 | |
|
| | 2747 | | /// <summary>Simplifies the mathematical expression.</summary> |
| | 2748 | | /// <returns>The simplified mathematical expression.</returns> |
| | 2749 | | public override Expression Simplify() |
| 0 | 2750 | | { |
| 0 | 2751 | | Expression LEFT = A.Simplify(); |
| 0 | 2752 | | Expression RIGHT = B.Simplify(); |
| | 2753 | | #region Computation |
| 0 | 2754 | | { // Rule: [A == B] => [C] where A is constant, B is constant, and C is A > B |
| 0 | 2755 | | if (LEFT is Constant A && RIGHT is Constant B) |
| 0 | 2756 | | { |
| 0 | 2757 | | return A.Simplify(this, A, B); |
| | 2758 | | } |
| 0 | 2759 | | } |
| | 2760 | | #endregion |
| 0 | 2761 | | return new GreaterThan(LEFT, RIGHT); |
| 0 | 2762 | | } |
| | 2763 | |
|
| | 2764 | | internal override Expression Simplify<T>(params Expression[] operands) |
| 0 | 2765 | | { |
| 0 | 2766 | | if (operands[0] is Constant<T> a && operands[1] is Constant<T> b) |
| 0 | 2767 | | { |
| 0 | 2768 | | return new Constant<bool>(GreaterThan(a.Value, b.Value)); |
| | 2769 | | } |
| 0 | 2770 | | return base.Simplify<T>(); |
| 0 | 2771 | | } |
| | 2772 | |
|
| | 2773 | | /// <summary>Clones this expression.</summary> |
| | 2774 | | /// <returns>A clone of this expression.</returns> |
| 0 | 2775 | | public override Expression Clone() => new GreaterThan(A.Clone(), B.Clone()); |
| | 2776 | |
|
| | 2777 | | /// <summary>Substitutes an expression for all occurences of a variable.</summary> |
| | 2778 | | /// <param name="variable">The variable to be substititued.</param> |
| | 2779 | | /// <param name="expression">The expression to substitute for each occurence of a variable.</param> |
| | 2780 | | /// <returns>The resulting expression of the substitution.</returns> |
| | 2781 | | public override Expression Substitute(string variable, Expression expression) => |
| 0 | 2782 | | new GreaterThan(A.Substitute(variable, expression), B.Substitute(variable, expression)); |
| | 2783 | |
|
| | 2784 | | /// <summary>Standard conversion to a string representation.</summary> |
| | 2785 | | /// <returns>The string represnetation of this expression.</returns> |
| 0 | 2786 | | public override string ToString() => A + " < " + B; |
| | 2787 | | } |
| | 2788 | |
|
| | 2789 | | #endregion |
| | 2790 | |
|
| | 2791 | | #region LessThanOrEqual |
| | 2792 | |
|
| | 2793 | | /// <summary>Represents a less than or equal to operation.</summary> |
| | 2794 | | [BinaryOperator("<=", OperatorPriority.Logical)] |
| | 2795 | | public class LessThanOrEqual : Binary, Operation.ILogical |
| | 2796 | | { |
| | 2797 | | /// <summary>Constructs a new less than or equal to operation.</summary> |
| | 2798 | | /// <param name="a">The left expression of the less than or equal to operation.</param> |
| | 2799 | | /// <param name="b">The right expression of the less than or equal to operation.</param> |
| 0 | 2800 | | public LessThanOrEqual(Expression a, Expression b) : base(a, b) { } |
| | 2801 | |
|
| | 2802 | | /// <summary>Simplifies the mathematical expression.</summary> |
| | 2803 | | /// <returns>The simplified mathematical expression.</returns> |
| | 2804 | | public override Expression Simplify() |
| 0 | 2805 | | { |
| 0 | 2806 | | Expression LEFT = A.Simplify(); |
| 0 | 2807 | | Expression RIGHT = B.Simplify(); |
| | 2808 | | #region Computation |
| 0 | 2809 | | { // Rule: [A == B] => [C] where A is constant, B is constant, and C is A <= B |
| 0 | 2810 | | if (LEFT is Constant A && RIGHT is Constant B) |
| 0 | 2811 | | { |
| 0 | 2812 | | return A.Simplify(this, A, B); |
| | 2813 | | } |
| 0 | 2814 | | } |
| | 2815 | | #endregion |
| 0 | 2816 | | return new LessThanOrEqual(LEFT, RIGHT); |
| 0 | 2817 | | } |
| | 2818 | |
|
| | 2819 | | internal override Expression Simplify<T>(params Expression[] operands) |
| 0 | 2820 | | { |
| 0 | 2821 | | if (operands[0] is Constant<T> a && operands[1] is Constant<T> b) |
| 0 | 2822 | | { |
| 0 | 2823 | | return new Constant<bool>(LessThanOrEqual(a.Value, b.Value)); |
| | 2824 | | } |
| 0 | 2825 | | return base.Simplify<T>(); |
| 0 | 2826 | | } |
| | 2827 | |
|
| | 2828 | | /// <summary>Clones this expression.</summary> |
| | 2829 | | /// <returns>A clone of this expression.</returns> |
| 0 | 2830 | | public override Expression Clone() => new LessThanOrEqual(A.Clone(), B.Clone()); |
| | 2831 | |
|
| | 2832 | | /// <summary>Substitutes an expression for all occurences of a variable.</summary> |
| | 2833 | | /// <param name="variable">The variable to be substititued.</param> |
| | 2834 | | /// <param name="expression">The expression to substitute for each occurence of a variable.</param> |
| | 2835 | | /// <returns>The resulting expression of the substitution.</returns> |
| | 2836 | | public override Expression Substitute(string variable, Expression expression) => |
| 0 | 2837 | | new LessThanOrEqual(A.Substitute(variable, expression), B.Substitute(variable, expression)); |
| | 2838 | |
|
| | 2839 | | /// <summary>Standard conversion to a string representation.</summary> |
| | 2840 | | /// <returns>The string represnetation of this expression.</returns> |
| 0 | 2841 | | public override string ToString() => A + " < " + B; |
| | 2842 | | } |
| | 2843 | |
|
| | 2844 | | #endregion |
| | 2845 | |
|
| | 2846 | | #region GreaterThanOrEqual |
| | 2847 | |
|
| | 2848 | | /// <summary>Represents a greater than or equal to operation.</summary> |
| | 2849 | | [BinaryOperator(">=", OperatorPriority.Logical)] |
| | 2850 | | public class GreaterThanOrEqual : Binary, Operation.ILogical |
| | 2851 | | { |
| | 2852 | | /// <summary>Constructs a new greater than or equal to operation.</summary> |
| | 2853 | | /// <param name="a">The left expression of the greater than or equal to operation.</param> |
| | 2854 | | /// <param name="b">The right expression of the greater than or equal to operation.</param> |
| 0 | 2855 | | public GreaterThanOrEqual(Expression a, Expression b) : base(a, b) { } |
| | 2856 | |
|
| | 2857 | | /// <summary>Simplifies the mathematical expression.</summary> |
| | 2858 | | /// <returns>The simplified mathematical expression.</returns> |
| | 2859 | | public override Expression Simplify() |
| 0 | 2860 | | { |
| 0 | 2861 | | Expression LEFT = A.Simplify(); |
| 0 | 2862 | | Expression RIGHT = B.Simplify(); |
| | 2863 | | #region Computation |
| 0 | 2864 | | { // Rule: [A == B] => [C] where A is constant, B is constant, and C is A >= B |
| 0 | 2865 | | if (LEFT is Constant A && RIGHT is Constant B) |
| 0 | 2866 | | { |
| 0 | 2867 | | return A.Simplify(this, A, B); |
| | 2868 | | } |
| 0 | 2869 | | } |
| | 2870 | | #endregion |
| 0 | 2871 | | return new GreaterThanOrEqual(LEFT, RIGHT); |
| 0 | 2872 | | } |
| | 2873 | |
|
| | 2874 | | internal override Expression Simplify<T>(params Expression[] operands) |
| 0 | 2875 | | { |
| 0 | 2876 | | if (operands[0] is Constant<T> a && operands[1] is Constant<T> b) |
| 0 | 2877 | | { |
| 0 | 2878 | | return new Constant<bool>(GreaterThanOrEqual(a.Value, b.Value)); |
| | 2879 | | } |
| 0 | 2880 | | return base.Simplify<T>(); |
| 0 | 2881 | | } |
| | 2882 | |
|
| | 2883 | | /// <summary>Clones this expression.</summary> |
| | 2884 | | /// <returns>A clone of this expression.</returns> |
| 0 | 2885 | | public override Expression Clone() => new GreaterThanOrEqual(A.Clone(), B.Clone()); |
| | 2886 | |
|
| | 2887 | | /// <summary>Substitutes an expression for all occurences of a variable.</summary> |
| | 2888 | | /// <param name="variable">The variable to be substititued.</param> |
| | 2889 | | /// <param name="expression">The expression to substitute for each occurence of a variable.</param> |
| | 2890 | | /// <returns>The resulting expression of the substitution.</returns> |
| | 2891 | | public override Expression Substitute(string variable, Expression expression) => |
| 0 | 2892 | | new GreaterThanOrEqual(A.Substitute(variable, expression), B.Substitute(variable, expression)); |
| | 2893 | |
|
| | 2894 | | /// <summary>Standard conversion to a string representation.</summary> |
| | 2895 | | /// <returns>The string represnetation of this expression.</returns> |
| 0 | 2896 | | public override string ToString() => A + " < " + B; |
| | 2897 | | } |
| | 2898 | |
|
| | 2899 | | #endregion |
| | 2900 | |
|
| | 2901 | | #endregion |
| | 2902 | |
|
| | 2903 | | #region Ternary + Inheriters |
| | 2904 | |
|
| | 2905 | | #region Ternary |
| | 2906 | |
|
| | 2907 | | #pragma warning disable CS0659 // Type overrides Object.Equals(object o) but does not override Object.GetHashCode() |
| | 2908 | | /// <summary>Abstract base class for ternary operations.</summary> |
| | 2909 | | public abstract class Ternary : Operation |
| | 2910 | | #pragma warning restore CS0659 // Type overrides Object.Equals(object o) but does not override Object.GetHashCode() |
| | 2911 | | { |
| | 2912 | | /// <summary>The first operand of the ternary operation.</summary> |
| 0 | 2913 | | public Expression A { get; set; } |
| | 2914 | | /// <summary>The second operand of the ternary operation.</summary> |
| 0 | 2915 | | public Expression B { get; set; } |
| | 2916 | | /// <summary>The third operand of the ternary operation.</summary> |
| 0 | 2917 | | public Expression C { get; set; } |
| | 2918 | |
|
| | 2919 | | /// <summary>Constructs a new ternary operation.</summary> |
| | 2920 | | /// <param name="a">The first operand of the ternary operation.</param> |
| | 2921 | | /// <param name="b">The second operand of the ternary operation.</param> |
| | 2922 | | /// <param name="c">The third operand of the ternary operation.</param> |
| 0 | 2923 | | public Ternary(Expression a, Expression b, Expression c) |
| 0 | 2924 | | { |
| 0 | 2925 | | A = a; |
| 0 | 2926 | | B = b; |
| 0 | 2927 | | C = c; |
| 0 | 2928 | | } |
| | 2929 | |
|
| | 2930 | | /// <summary>Standard equality check.</summary> |
| | 2931 | | /// <param name="b">The object to check for equality with.</param> |
| | 2932 | | /// <returns>True if equal. False if not.</returns> |
| | 2933 | | public override bool Equals(object? b) |
| 0 | 2934 | | { |
| 0 | 2935 | | if (b is not null && GetType() == b.GetType()) |
| 0 | 2936 | | { |
| 0 | 2937 | | return A.Equals(((Ternary)b).A) && B.Equals(((Ternary)b).B) && C.Equals(((Ternary)b).C); |
| | 2938 | | } |
| 0 | 2939 | | return false; |
| 0 | 2940 | | } |
| | 2941 | | } |
| | 2942 | |
|
| | 2943 | | #endregion |
| | 2944 | |
|
| | 2945 | | #endregion |
| | 2946 | |
|
| | 2947 | | #region Multinary + Inheriters |
| | 2948 | |
|
| | 2949 | | #region Multinary |
| | 2950 | |
|
| | 2951 | | /// <summary>Abstract base class for multinary operations.</summary> |
| | 2952 | | public abstract class Multinary : Operation |
| | 2953 | | { |
| | 2954 | | /// <summary>The operands of the multinary operation.</summary> |
| 0 | 2955 | | public Expression[] Operands { get; set; } |
| | 2956 | |
|
| | 2957 | | /// <summary>Constructs a new multinary operation.</summary> |
| | 2958 | | /// <param name="operands">The operands of the multinary operation.</param> |
| 0 | 2959 | | public Multinary(Expression[] operands) |
| 0 | 2960 | | { |
| 0 | 2961 | | Operands = operands; |
| 0 | 2962 | | } |
| | 2963 | | } |
| | 2964 | |
|
| | 2965 | | #endregion |
| | 2966 | |
|
| | 2967 | | #endregion |
| | 2968 | |
|
| | 2969 | | #endregion |
| | 2970 | |
|
| | 2971 | | #endregion |
| | 2972 | |
|
| | 2973 | | #region Parsers |
| | 2974 | |
|
| | 2975 | | // Notes: |
| | 2976 | | // Parsing uses the Expression class hierarchy with the class atributes to build |
| | 2977 | | // a library of parsing constants via reflection. Once built, the parsing library |
| | 2978 | | // is used as a reference to contruct the proper Expression type via a contruction |
| | 2979 | | // delegate. |
| | 2980 | |
|
| | 2981 | | #region Runtime Built Parsing Libary |
| | 2982 | |
|
| | 2983 | | // Library Building Fields |
| | 2984 | | internal static bool ParseableLibraryBuilt; |
| 1 | 2985 | | internal static readonly object ParseableLibraryLock = new(); |
| | 2986 | | // Regex Expressions |
| | 2987 | | internal const string ParenthesisPattern = @"\(.*\)"; |
| | 2988 | | internal static string? ParsableOperationsRegexPattern; |
| | 2989 | | internal static string? ParsableOperatorsRegexPattern; |
| | 2990 | | internal static string? ParsableKnownConstantsRegexPattern; |
| | 2991 | | internal static string? SpecialStringsPattern; |
| | 2992 | | // Operation Refrences |
| | 2993 | | internal static System.Collections.Generic.Dictionary<string, Func<Expression, Unary>>? ParsableUnaryOperations; |
| | 2994 | | internal static System.Collections.Generic.Dictionary<string, Func<Expression, Expression, Binary>>? ParsableBinaryOpe |
| | 2995 | | internal static System.Collections.Generic.Dictionary<string, Func<Expression, Expression, Expression, Ternary>>? Pars |
| | 2996 | | internal static System.Collections.Generic.Dictionary<string, Func<Expression[], Multinary>>? ParsableMultinaryOperati |
| | 2997 | | // Operator References |
| | 2998 | | internal static System.Collections.Generic.Dictionary<string, (OperatorPriority, Func<Expression, Unary>)>? ParsableLe |
| | 2999 | | internal static System.Collections.Generic.Dictionary<string, (OperatorPriority, Func<Expression, Unary>)>? ParsableRi |
| | 3000 | | internal static System.Collections.Generic.Dictionary<string, (OperatorPriority, Func<Expression, Expression, Binary>) |
| | 3001 | | // Known Constant References |
| | 3002 | | internal static System.Collections.Generic.Dictionary<string, Func<KnownConstantOfUnknownType>>? ParsableKnownConstant |
| | 3003 | |
|
| | 3004 | | #region Reflection Code (Actually Building the Parsing Library) |
| | 3005 | |
|
| | 3006 | | internal static void BuildParsableOperationLibrary() |
| 1 | 3007 | | { |
| 1 | 3008 | | lock (ParseableLibraryLock) |
| 1 | 3009 | | { |
| 1 | 3010 | | if (ParseableLibraryBuilt) |
| 0 | 3011 | | { |
| 0 | 3012 | | return; |
| | 3013 | | } |
| | 3014 | |
|
| | 3015 | | // Unary Operations |
| 1 | 3016 | | ParsableUnaryOperations = new System.Collections.Generic.Dictionary<string, Func<Expression, Unary>>(); |
| 1 | 3017 | | ParsableLeftUnaryOperators = new System.Collections.Generic.Dictionary<string, (OperatorPriority, Func<Expression, |
| 1 | 3018 | | ParsableRightUnaryOperators = new System.Collections.Generic.Dictionary<string, (OperatorPriority, Func<Expression |
| 43 | 3019 | | foreach (Type type in Assembly.GetExecutingAssembly().GetDerivedTypes<Unary>().Where(x => !x.IsAbstract)) |
| 13 | 3020 | | { |
| 13 | 3021 | | ConstructorInfo? constructorInfo = type.GetConstructor(Ɐ(typeof(Expression))); |
| 13 | 3022 | | if (constructorInfo is null) |
| 0 | 3023 | | { |
| 0 | 3024 | | throw new TowelBugException($"Could not find a {nameof(ConstructorInfo)} when building parsing library"); |
| | 3025 | | } |
| 13 | 3026 | | ParameterExpression A = System.Linq.Expressions.Expression.Parameter(typeof(Expression)); |
| 13 | 3027 | | NewExpression newExpression = System.Linq.Expressions.Expression.New(constructorInfo, A); |
| 13 | 3028 | | Func<Expression, Unary> newFunction = System.Linq.Expressions.Expression.Lambda<Func<Expression, Unary>>(newExpr |
| 13 | 3029 | | string operationName = type.Name; |
| 13 | 3030 | | if (operationName.Contains('+')) |
| 0 | 3031 | | { |
| 0 | 3032 | | int index = operationName.LastIndexOf('+'); |
| 0 | 3033 | | operationName = operationName[(index + 1)..]; |
| 0 | 3034 | | } |
| 13 | 3035 | | ParsableUnaryOperations.Add(operationName.ToLower(), newFunction); |
| 13 | 3036 | | OperationAttribute? operationAttribute = type.GetCustomAttribute<OperationAttribute>(); |
| 13 | 3037 | | if (operationAttribute is not null) |
| 1 | 3038 | | { |
| 5 | 3039 | | foreach (string representation in operationAttribute.Representations) |
| 1 | 3040 | | { |
| 1 | 3041 | | ParsableUnaryOperations.Add(representation.ToLower(), newFunction); |
| 1 | 3042 | | } |
| 1 | 3043 | | } |
| | 3044 | |
|
| | 3045 | | // Left Unary Operators |
| 41 | 3046 | | foreach (LeftUnaryOperatorAttribute @operator in type.GetCustomAttributes<LeftUnaryOperatorAttribute>()) |
| 1 | 3047 | | { |
| 1 | 3048 | | ParsableLeftUnaryOperators.Add(@operator.Representation.ToLower(), (@operator.Priority, newFunction)); |
| 1 | 3049 | | } |
| | 3050 | |
|
| | 3051 | | // Right Unary Operators |
| 41 | 3052 | | foreach (RightUnaryOperatorAttribute @operator in type.GetCustomAttributes<RightUnaryOperatorAttribute>()) |
| 1 | 3053 | | { |
| 1 | 3054 | | ParsableRightUnaryOperators.Add(@operator.Representation.ToLower(), (@operator.Priority, newFunction)); |
| 1 | 3055 | | } |
| 13 | 3056 | | } |
| | 3057 | |
|
| | 3058 | | // Binary Operations |
| 1 | 3059 | | ParsableBinaryOperations = new System.Collections.Generic.Dictionary<string, Func<Expression, Expression, Binary>> |
| 1 | 3060 | | ParsableBinaryOperators = new System.Collections.Generic.Dictionary<string, (OperatorPriority, Func<Expression, Ex |
| 41 | 3061 | | foreach (Type type in Assembly.GetExecutingAssembly().GetDerivedTypes<Binary>().Where(x => !x.IsAbstract)) |
| 12 | 3062 | | { |
| 12 | 3063 | | ConstructorInfo? constructorInfo = type.GetConstructor(Ɐ(typeof(Expression), typeof(Expression))); |
| 12 | 3064 | | if (constructorInfo is null) |
| 0 | 3065 | | { |
| 0 | 3066 | | throw new TowelBugException($"Could not find a {nameof(ConstructorInfo)} when building parsing library"); |
| | 3067 | | } |
| 12 | 3068 | | ParameterExpression A = System.Linq.Expressions.Expression.Parameter(typeof(Expression)); |
| 12 | 3069 | | ParameterExpression B = System.Linq.Expressions.Expression.Parameter(typeof(Expression)); |
| 12 | 3070 | | NewExpression newExpression = System.Linq.Expressions.Expression.New(constructorInfo, A, B); |
| 12 | 3071 | | Func<Expression, Expression, Binary> newFunction = System.Linq.Expressions.Expression.Lambda<Func<Expression, Ex |
| 12 | 3072 | | string operationName = type.Name; |
| 12 | 3073 | | if (operationName.Contains('+')) |
| 0 | 3074 | | { |
| 0 | 3075 | | int index = operationName.LastIndexOf('+'); |
| 0 | 3076 | | operationName = operationName[(index + 1)..]; |
| 0 | 3077 | | } |
| 12 | 3078 | | ParsableBinaryOperations.Add(operationName.ToLower(), newFunction); |
| 12 | 3079 | | OperationAttribute? operationAttribute = type.GetCustomAttribute<OperationAttribute>(); |
| 12 | 3080 | | if (operationAttribute is not null) |
| 0 | 3081 | | { |
| 0 | 3082 | | foreach (string representation in operationAttribute.Representations) |
| 0 | 3083 | | { |
| 0 | 3084 | | ParsableBinaryOperations.Add(representation.ToLower(), newFunction); |
| 0 | 3085 | | } |
| 0 | 3086 | | } |
| | 3087 | |
|
| | 3088 | | // Binary Operators |
| 58 | 3089 | | foreach (BinaryOperatorAttribute @operator in type.GetCustomAttributes<BinaryOperatorAttribute>()) |
| 11 | 3090 | | { |
| 11 | 3091 | | ParsableBinaryOperators.Add(@operator.Representation.ToLower(), (@operator.Priority, newFunction)); |
| 11 | 3092 | | } |
| 12 | 3093 | | } |
| | 3094 | |
|
| | 3095 | | // Ternary Operations |
| 1 | 3096 | | ParsableTernaryOperations = new System.Collections.Generic.Dictionary<string, Func<Expression, Expression, Express |
| 3 | 3097 | | foreach (Type type in Assembly.GetExecutingAssembly().GetDerivedTypes<Ternary>().Where(x => !x.IsAbstract)) |
| 0 | 3098 | | { |
| 0 | 3099 | | ConstructorInfo? constructorInfo = type.GetConstructor(Ɐ(typeof(Expression), typeof(Expression), typeof(Expressi |
| 0 | 3100 | | if (constructorInfo is null) |
| 0 | 3101 | | { |
| 0 | 3102 | | throw new TowelBugException($"Could not find a {nameof(ConstructorInfo)} when building parsing library"); |
| | 3103 | | } |
| 0 | 3104 | | ParameterExpression A = System.Linq.Expressions.Expression.Parameter(typeof(Expression)); |
| 0 | 3105 | | ParameterExpression B = System.Linq.Expressions.Expression.Parameter(typeof(Expression)); |
| 0 | 3106 | | ParameterExpression C = System.Linq.Expressions.Expression.Parameter(typeof(Expression)); |
| 0 | 3107 | | NewExpression newExpression = System.Linq.Expressions.Expression.New(constructorInfo, A, B, C); |
| 0 | 3108 | | Func<Expression, Expression, Expression, Ternary> newFunction = System.Linq.Expressions.Expression.Lambda<Func<E |
| 0 | 3109 | | string operationName = type.Name; |
| 0 | 3110 | | if (operationName.Contains('+')) |
| 0 | 3111 | | { |
| 0 | 3112 | | int index = operationName.LastIndexOf('+'); |
| 0 | 3113 | | operationName = operationName[(index + 1)..]; |
| 0 | 3114 | | } |
| 0 | 3115 | | ParsableTernaryOperations.Add(operationName.ToLower(), newFunction); |
| 0 | 3116 | | OperationAttribute? operationAttribute = type.GetCustomAttribute<OperationAttribute>(); |
| 0 | 3117 | | if (operationAttribute is not null) |
| 0 | 3118 | | { |
| 0 | 3119 | | foreach (string representation in operationAttribute.Representations) |
| 0 | 3120 | | { |
| 0 | 3121 | | ParsableTernaryOperations.Add(representation.ToLower(), newFunction); |
| 0 | 3122 | | } |
| 0 | 3123 | | } |
| 0 | 3124 | | } |
| | 3125 | |
|
| | 3126 | | // Multinary Operations |
| 1 | 3127 | | ParsableMultinaryOperations = new System.Collections.Generic.Dictionary<string, Func<Expression[], Multinary>>(); |
| 3 | 3128 | | foreach (Type type in Assembly.GetExecutingAssembly().GetDerivedTypes<Multinary>().Where(x => !x.IsAbstract)) |
| 0 | 3129 | | { |
| 0 | 3130 | | ConstructorInfo? constructorInfo = type.GetConstructor(Ɐ(typeof(Expression[]))); |
| 0 | 3131 | | if (constructorInfo is null) |
| 0 | 3132 | | { |
| 0 | 3133 | | throw new TowelBugException($"Could not find a {nameof(ConstructorInfo)} when building parsing library"); |
| | 3134 | | } |
| 0 | 3135 | | ParameterExpression A = System.Linq.Expressions.Expression.Parameter(typeof(Expression[])); |
| 0 | 3136 | | NewExpression newExpression = System.Linq.Expressions.Expression.New(constructorInfo, A); |
| 0 | 3137 | | Func<Expression[], Multinary> newFunction = System.Linq.Expressions.Expression.Lambda<Func<Expression[], Multina |
| 0 | 3138 | | string operationName = type.Name; |
| 0 | 3139 | | if (operationName.Contains('+')) |
| 0 | 3140 | | { |
| 0 | 3141 | | int index = operationName.LastIndexOf('+'); |
| 0 | 3142 | | operationName = operationName[(index + 1)..]; |
| 0 | 3143 | | } |
| 0 | 3144 | | ParsableMultinaryOperations.Add(operationName.ToLower(), newFunction); |
| 0 | 3145 | | OperationAttribute? operationAttribute = type.GetCustomAttribute<OperationAttribute>(); |
| 0 | 3146 | | if (operationAttribute is not null) |
| 0 | 3147 | | { |
| 0 | 3148 | | foreach (string representation in operationAttribute.Representations) |
| 0 | 3149 | | { |
| 0 | 3150 | | ParsableMultinaryOperations.Add(representation.ToLower(), newFunction); |
| 0 | 3151 | | } |
| 0 | 3152 | | } |
| 0 | 3153 | | } |
| | 3154 | |
|
| | 3155 | | // Known Constants |
| 1 | 3156 | | ParsableKnownConstants = new System.Collections.Generic.Dictionary<string, Func<KnownConstantOfUnknownType>>(); |
| 18 | 3157 | | foreach (Type type in Assembly.GetExecutingAssembly().GetDerivedTypes<KnownConstantOfUnknownType>().Where(x => !x. |
| 5 | 3158 | | { |
| 5 | 3159 | | ConstructorInfo? constructorInfo = type.GetConstructor(Type.EmptyTypes); |
| 5 | 3160 | | if (constructorInfo is null) |
| 0 | 3161 | | { |
| 0 | 3162 | | throw new TowelBugException($"Could not find a {nameof(ConstructorInfo)} when building parsing library"); |
| | 3163 | | } |
| 5 | 3164 | | NewExpression newExpression = System.Linq.Expressions.Expression.New(constructorInfo); |
| 5 | 3165 | | Func<KnownConstantOfUnknownType> newFunction = System.Linq.Expressions.Expression.Lambda<Func<KnownConstantOfUnk |
| | 3166 | | // string knownConstant = type.ConvertToCsharpSource(); |
| | 3167 | | // if (knownConstant.Contains('+')) |
| | 3168 | | // { |
| | 3169 | | // int index = knownConstant.LastIndexOf('+'); |
| | 3170 | | // knownConstant = knownConstant.Substring(index + 1); |
| | 3171 | | // } |
| | 3172 | | // ParsableKnownConstants.Add(knownConstant.ToLower(), newFunction); |
| 5 | 3173 | | KnownConstantAttribute? knownConstantAttribute = type.GetCustomAttribute<KnownConstantAttribute>(); |
| 5 | 3174 | | if (knownConstantAttribute is not null) |
| 1 | 3175 | | { |
| 5 | 3176 | | foreach (string representation in knownConstantAttribute.Representations) |
| 1 | 3177 | | { |
| 1 | 3178 | | ParsableKnownConstants.Add(representation.ToLower(), newFunction); |
| 1 | 3179 | | } |
| 1 | 3180 | | } |
| 5 | 3181 | | } |
| | 3182 | |
|
| | 3183 | | // Build a regex to match any operation |
| 1 | 3184 | | System.Collections.Generic.IEnumerable<string> operations = |
| 1 | 3185 | | ParsableUnaryOperations.Keys.Concat( |
| 1 | 3186 | | ParsableBinaryOperations.Keys.Concat( |
| 1 | 3187 | | ParsableTernaryOperations.Keys.Concat( |
| 27 | 3188 | | ParsableMultinaryOperations.Keys))).Select(x => Regex.Escape(x)); |
| 1 | 3189 | | ParsableOperationsRegexPattern = string.Join(@"\s*\(.*\)|", operations) + @"\s *\(.*\)"; |
| | 3190 | |
|
| | 3191 | | // Build a regex to match any operator |
| 1 | 3192 | | System.Collections.Generic.IEnumerable<string> operators = |
| 1 | 3193 | | ParsableLeftUnaryOperators.Keys.Concat( |
| 1 | 3194 | | ParsableRightUnaryOperators.Keys.Concat( |
| 27 | 3195 | | ParsableBinaryOperators.Keys)).Select(x => Regex.Escape(x)); |
| 1 | 3196 | | ParsableOperatorsRegexPattern = string.Join("|", operators); |
| | 3197 | |
|
| 1 | 3198 | | System.Collections.Generic.IEnumerable<string> knownConstants = |
| 2 | 3199 | | ParsableKnownConstants.Keys.Select(x => Regex.Escape(x)); |
| 1 | 3200 | | ParsableKnownConstantsRegexPattern = string.Join("|", knownConstants); |
| | 3201 | |
|
| 1 | 3202 | | SpecialStringsPattern = string.Join("|", operators.Append(Regex.Escape("(")).Append(Regex.Escape(")"))); |
| | 3203 | |
|
| 1 | 3204 | | ParseableLibraryBuilt = true; |
| 1 | 3205 | | } |
| 1 | 3206 | | } |
| | 3207 | |
|
| | 3208 | | #endregion |
| | 3209 | |
|
| | 3210 | | #endregion |
| | 3211 | |
|
| | 3212 | | #region System.Linq.Expression |
| | 3213 | |
|
| | 3214 | | /// <summary>Parses a mathematical expression from a linq expression.</summary> |
| | 3215 | | /// <param name="e">The linq expression to parse.</param> |
| | 3216 | | /// <returns>The parsed symbolic mathematics linq expression.</returns> |
| | 3217 | | public static Expression Parse(System.Linq.Expressions.Expression e) |
| 0 | 3218 | | { |
| | 3219 | | Expression ParseRecursive(System.Linq.Expressions.Expression expression) |
| 0 | 3220 | | { |
| 0 | 3221 | | return expression.NodeType switch |
| 0 | 3222 | | { |
| 0 | 3223 | | ExpressionType.Lambda => ParseRecursive(((LambdaExpression)expression).Body), |
| 0 | 3224 | | ExpressionType.Constant => Constant.BuildGeneric(((ConstantExpression)expression).Value ?? throw new ArgumentExc |
| 0 | 3225 | | ExpressionType.Parameter => new Variable(((ParameterExpression)expression).Name ?? throw new ArgumentException(p |
| 0 | 3226 | | ExpressionType.Negate => new Negate(ParseRecursive(((UnaryExpression)expression).Operand)), |
| 0 | 3227 | | ExpressionType.UnaryPlus => ParseRecursive(((UnaryExpression)expression).Operand), |
| 0 | 3228 | | ExpressionType.Add => new Add(ParseRecursive(((BinaryExpression)expression).Left), ParseRecursive(((BinaryExpres |
| 0 | 3229 | | ExpressionType.Subtract => new Subtract(ParseRecursive(((BinaryExpression)expression).Left), ParseRecursive(((Bi |
| 0 | 3230 | | ExpressionType.Multiply => new Multiply(ParseRecursive(((BinaryExpression)expression).Left), ParseRecursive(((Bi |
| 0 | 3231 | | ExpressionType.Divide => new Divide(ParseRecursive(((BinaryExpression)expression).Left), ParseRecursive(((Binary |
| 0 | 3232 | | ExpressionType.Power => new Power(ParseRecursive(((BinaryExpression)expression).Left), ParseRecursive(((BinaryEx |
| 0 | 3233 | | ExpressionType.Call => ParseMethodCall((MethodCallExpression)expression), |
| 0 | 3234 | | _ => throw new ArgumentException("The expression could not be parsed.", nameof(e)), |
| 0 | 3235 | | }; |
| 0 | 3236 | | } |
| | 3237 | |
|
| | 3238 | | Expression ParseMethodCall(MethodCallExpression methodCallExpression) |
| 0 | 3239 | | { |
| 0 | 3240 | | MethodInfo methodInfo = methodCallExpression.Method; |
| 0 | 3241 | | if (methodInfo is null) |
| 0 | 3242 | | { |
| 0 | 3243 | | throw new ArgumentException("The expression could not be parsed.", nameof(e)); |
| | 3244 | | } |
| | 3245 | |
|
| 0 | 3246 | | Expression[]? arguments = null; |
| 0 | 3247 | | if (methodCallExpression.Arguments is not null) |
| 0 | 3248 | | { |
| 0 | 3249 | | arguments = new Expression[methodCallExpression.Arguments.Count]; |
| 0 | 3250 | | for (int i = 0; i < arguments.Length; i++) |
| 0 | 3251 | | arguments[i] = ParseRecursive(methodCallExpression.Arguments[i]); |
| 0 | 3252 | | } |
| | 3253 | |
|
| 0 | 3254 | | if (!ParseableLibraryBuilt) |
| 0 | 3255 | | { |
| 0 | 3256 | | BuildParsableOperationLibrary(); |
| 0 | 3257 | | } |
| | 3258 | |
|
| 0 | 3259 | | string operation = methodInfo.Name.ToLower(); |
| | 3260 | |
|
| 0 | 3261 | | if (arguments is null) |
| 0 | 3262 | | { |
| 0 | 3263 | | throw new TowelBugException("zero parameter operations are not yet implemented"); |
| | 3264 | | } |
| | 3265 | |
|
| 0 | 3266 | | switch (arguments.Length) |
| | 3267 | | { |
| | 3268 | | case 1: |
| 0 | 3269 | | if (ParsableUnaryOperations!.TryGetValue(operation, out var newUnaryFunction)) |
| 0 | 3270 | | { |
| 0 | 3271 | | return newUnaryFunction(arguments[0]); |
| | 3272 | | } |
| 0 | 3273 | | break; |
| | 3274 | | case 2: |
| 0 | 3275 | | if (ParsableBinaryOperations!.TryGetValue(operation, out var newBinaryFunction)) |
| 0 | 3276 | | { |
| 0 | 3277 | | return newBinaryFunction(arguments[0], arguments[1]); |
| | 3278 | | } |
| 0 | 3279 | | break; |
| | 3280 | | case 3: |
| 0 | 3281 | | if (ParsableTernaryOperations!.TryGetValue(operation, out var newTernaryFunction)) |
| 0 | 3282 | | { |
| 0 | 3283 | | return newTernaryFunction(arguments[0], arguments[1], arguments[2]); |
| | 3284 | | } |
| 0 | 3285 | | break; |
| | 3286 | | } |
| | 3287 | |
|
| 0 | 3288 | | if (ParsableMultinaryOperations!.TryGetValue(operation, out var newMultinaryFunction)) |
| 0 | 3289 | | { |
| 0 | 3290 | | return newMultinaryFunction(arguments); |
| | 3291 | | } |
| | 3292 | |
|
| 0 | 3293 | | throw new ArgumentException("The expression could not be parsed.", nameof(e)); |
| 0 | 3294 | | } |
| | 3295 | |
|
| | 3296 | | try |
| 0 | 3297 | | { |
| 0 | 3298 | | return ParseRecursive(e); |
| | 3299 | | } |
| 0 | 3300 | | catch (ArithmeticException arithmeticException) |
| 0 | 3301 | | { |
| 0 | 3302 | | throw new ArgumentException("The expression could not be parsed.", nameof(e), arithmeticException); |
| | 3303 | | } |
| 0 | 3304 | | } |
| | 3305 | |
|
| | 3306 | | #endregion |
| | 3307 | |
|
| | 3308 | | #region string |
| | 3309 | |
|
| | 3310 | | /// <summary>Parses a symbolic methematics expression with the assumption that it will simplify to a constant.</summar |
| | 3311 | | /// <typeparam name="T">The generic numerical type to recieve as the outputted type.</typeparam> |
| | 3312 | | /// <param name="string">The string to be parse.</param> |
| | 3313 | | /// <param name="tryParse">A function for parsing numerical values into the provided generic type.</param> |
| | 3314 | | /// <returns>The parsed expression simplified down to a constant value.</returns> |
| | 3315 | | public static T ParseAndSimplifyToConstant<T>(string @string, Func<string, (bool Success, T? Value)>? tryParse = null) |
| 0 | 3316 | | { |
| 0 | 3317 | | tryParse ??= Statics.TryParse<T>; |
| 0 | 3318 | | var simplified = Parse(@string, tryParse).Simplify(); |
| 0 | 3319 | | if (simplified is Constant<T> constant) |
| 0 | 3320 | | { |
| 0 | 3321 | | return constant.Value; |
| | 3322 | | } |
| | 3323 | | else |
| 0 | 3324 | | { |
| 0 | 3325 | | throw new ArgumentException(paramName: nameof(@string), message: $"{nameof(@string)} could not be simplified to a |
| | 3326 | | } |
| 0 | 3327 | | } |
| | 3328 | |
|
| | 3329 | | /// <summary>Parses a string into a Towel.Mathematics.Symbolics expression tree.</summary> |
| | 3330 | | /// <typeparam name="T">The type to convert any constants into (ex: float, double, etc).</typeparam> |
| | 3331 | | /// <param name="string">The expression string to parse.</param> |
| | 3332 | | /// <param name="tryParse">A parsing function for the provided generic type. This is optional, but highly recommended. |
| | 3333 | | /// <returns>The parsed Towel.Mathematics.Symbolics expression tree.</returns> |
| | 3334 | | public static Expression Parse<T>(string @string, Func<string, (bool Success, T? Value)>? tryParse = null) |
| 330 | 3335 | | { |
| 330 | 3336 | | tryParse ??= Statics.TryParse<T>; |
| | 3337 | | // Build The Parsing Library |
| 330 | 3338 | | if (!ParseableLibraryBuilt) |
| 1 | 3339 | | { |
| 1 | 3340 | | BuildParsableOperationLibrary(); |
| 1 | 3341 | | } |
| | 3342 | | // Error Handling |
| 330 | 3343 | | if (string.IsNullOrWhiteSpace(@string)) |
| 0 | 3344 | | { |
| 0 | 3345 | | throw new ArgumentException("The expression could not be parsed. { " + @string + " }", nameof(@string)); |
| | 3346 | | } |
| | 3347 | | // Trim |
| 330 | 3348 | | @string = @string.Trim(); |
| | 3349 | | // Parse The Next Non-Nested Operator If One Exist |
| 330 | 3350 | | if (TryParseNonNestedOperatorExpression<T>(@string, tryParse, out Expression? ParsedNonNestedOperatorExpression)) |
| 86 | 3351 | | { |
| 86 | 3352 | | return ParsedNonNestedOperatorExpression!; |
| | 3353 | | } |
| | 3354 | | // Parse The Next Parenthesis If One Exists |
| 244 | 3355 | | if (TryParseParenthesisExpression<T>(@string, tryParse, out Expression? ParsedParenthesisExpression)) |
| 12 | 3356 | | { |
| 12 | 3357 | | return ParsedParenthesisExpression!; |
| | 3358 | | } |
| | 3359 | | // Parse The Next Operation If One Exists |
| 232 | 3360 | | if (TryParseOperationExpression<T>(@string, tryParse, out Expression? ParsedOperationExpression)) |
| 0 | 3361 | | { |
| 0 | 3362 | | return ParsedOperationExpression!; |
| | 3363 | | } |
| | 3364 | | // Parse The Next Set Of Variables If Any Exist |
| 232 | 3365 | | if (TryParseVariablesExpression<T>(@string, tryParse, out Expression? ParsedVeriablesExpression)) |
| 0 | 3366 | | { |
| 0 | 3367 | | return ParsedVeriablesExpression!; |
| | 3368 | | } |
| | 3369 | | // Parse The Next Known Constant Expression If Any Exist |
| 232 | 3370 | | if (TryParseKnownConstantExpression<T>(@string, tryParse, out Expression? ParsedKnownConstantExpression)) |
| 18 | 3371 | | { |
| 18 | 3372 | | return ParsedKnownConstantExpression!; |
| | 3373 | | } |
| | 3374 | | // Parse The Next Constant Expression If Any Exist |
| 214 | 3375 | | if (TryParseConstantExpression<T>(@string, tryParse, out Expression? ParsedConstantExpression)) |
| 214 | 3376 | | { |
| 214 | 3377 | | return ParsedConstantExpression!; |
| | 3378 | | } |
| | 3379 | | // Invalid Or Non-Supported Expression |
| 0 | 3380 | | throw new ArgumentException("The expression could not be parsed. { " + @string + " }", nameof(@string)); |
| 330 | 3381 | | } |
| | 3382 | |
|
| | 3383 | | internal static bool TryParseNonNestedOperatorExpression<T>(string @string, Func<string, (bool Success, T? Value)> try |
| 330 | 3384 | | { |
| | 3385 | | // Try to match the operators pattern built at runtime based on the symbolic tree hierarchy |
| 330 | 3386 | | MatchCollection operatorMatches = Regex.Matches(@string, ParsableOperatorsRegexPattern!, RegexOptions.RightToLeft); |
| 330 | 3387 | | MatchCollection specialStringMatches = Regex.Matches(@string, SpecialStringsPattern!, RegexOptions.RightToLeft); |
| 330 | 3388 | | if (operatorMatches.Count > 0) |
| 98 | 3389 | | { |
| | 3390 | | // Find the first operator with the highest available priority |
| 98 | 3391 | | Match? @operator = null; |
| 98 | 3392 | | OperatorPriority priority = default; |
| 98 | 3393 | | int currentOperatorMatch = 0; |
| 98 | 3394 | | int scope = 0; |
| 98 | 3395 | | bool isUnaryLeftOperator = false; |
| 98 | 3396 | | bool isUnaryRightOperator = false; |
| 98 | 3397 | | bool isBinaryOperator = false; |
| 894 | 3398 | | for (int i = @string.Length - 1; i >= 0; i--) |
| 447 | 3399 | | { |
| 447 | 3400 | | switch (@string[i]) |
| | 3401 | | { |
| 64 | 3402 | | case ')': scope++; break; |
| 40 | 3403 | | case '(': scope--; break; |
| | 3404 | | } |
| | 3405 | |
|
| | 3406 | | // Handle Input Errors |
| 447 | 3407 | | if (scope < 0) |
| 0 | 3408 | | { |
| 0 | 3409 | | throw new ArgumentException("The expression could not be parsed. { " + @string + " }", nameof(@string)); |
| | 3410 | | } |
| | 3411 | |
|
| 447 | 3412 | | Match currentMatch = operatorMatches[currentOperatorMatch]; |
| 447 | 3413 | | if (currentMatch.Index == i) |
| 136 | 3414 | | { |
| 136 | 3415 | | if (scope is 0) |
| 104 | 3416 | | { |
| 104 | 3417 | | Match? previousMatch = currentOperatorMatch != 0 ? operatorMatches[currentOperatorMatch - 1] : null; |
| 104 | 3418 | | Match? nextMatch = currentOperatorMatch != operatorMatches.Count - 1 ? operatorMatches[currentOperatorMatch |
| | 3419 | |
|
| | 3420 | | // We found an operator in the current scope |
| | 3421 | | // Now we need to determine if it is a unary-left, unary-right, or binary operator |
| | 3422 | |
|
| | 3423 | | bool IsUnaryLeftOperator() |
| 104 | 3424 | | { |
| 104 | 3425 | | if (!ParsableLeftUnaryOperators!.ContainsKey(currentMatch.Value)) |
| 84 | 3426 | | { |
| 84 | 3427 | | return false; |
| | 3428 | | } |
| | 3429 | |
|
| 20 | 3430 | | int rightIndex = currentMatch.Index - currentMatch.Length + 1; |
| 20 | 3431 | | if (rightIndex <= 0) |
| 5 | 3432 | | { |
| 5 | 3433 | | return true; |
| | 3434 | | } |
| 15 | 3435 | | Match? leftSpecialMatch = null; |
| 109 | 3436 | | foreach (Match match in specialStringMatches) |
| 34 | 3437 | | { |
| 34 | 3438 | | if (match.Index < currentMatch.Index) |
| 4 | 3439 | | { |
| 4 | 3440 | | leftSpecialMatch = match; |
| 4 | 3441 | | break; |
| | 3442 | | } |
| 30 | 3443 | | } |
| 15 | 3444 | | if (leftSpecialMatch is null) |
| 11 | 3445 | | { |
| 11 | 3446 | | string substring = @string[..rightIndex]; |
| 11 | 3447 | | return string.IsNullOrWhiteSpace(substring); |
| | 3448 | | } |
| 4 | 3449 | | else if (ParsableRightUnaryOperators!.ContainsKey(leftSpecialMatch.Value)) // This will need to be fixed i |
| 1 | 3450 | | { |
| 1 | 3451 | | return false; |
| | 3452 | | } |
| | 3453 | | else |
| 3 | 3454 | | { |
| 3 | 3455 | | int leftIndex = leftSpecialMatch.Index + 1; |
| 3 | 3456 | | string substring = @string[leftIndex..rightIndex]; |
| 3 | 3457 | | return string.IsNullOrWhiteSpace(substring); |
| | 3458 | | } |
| 104 | 3459 | | } |
| | 3460 | |
|
| | 3461 | | bool IsUnaryRightOperator() |
| 98 | 3462 | | { |
| 98 | 3463 | | if (!ParsableRightUnaryOperators!.ContainsKey(currentMatch.Value)) |
| 80 | 3464 | | { |
| 80 | 3465 | | return false; |
| | 3466 | | } |
| | 3467 | |
|
| 18 | 3468 | | int leftIndex = currentMatch.Index; |
| 18 | 3469 | | if (leftIndex >= @string.Length - 1) |
| 14 | 3470 | | { |
| 14 | 3471 | | return true; |
| | 3472 | | } |
| 4 | 3473 | | Match? rightSpecialMatch = null; |
| 24 | 3474 | | foreach (Match match in specialStringMatches) |
| 8 | 3475 | | { |
| 8 | 3476 | | if (match.Index <= currentMatch.Index) |
| 4 | 3477 | | { |
| 4 | 3478 | | break; |
| | 3479 | | } |
| 4 | 3480 | | rightSpecialMatch = match; |
| 4 | 3481 | | } |
| 4 | 3482 | | if (rightSpecialMatch is null) |
| 0 | 3483 | | { |
| 0 | 3484 | | return string.IsNullOrWhiteSpace(@string[(leftIndex + 1)..]); |
| | 3485 | | } |
| | 3486 | | else |
| 4 | 3487 | | { |
| 4 | 3488 | | int rightIndex = rightSpecialMatch.Index - rightSpecialMatch.Length; |
| 4 | 3489 | | return string.IsNullOrWhiteSpace(@string[leftIndex..rightIndex]); |
| | 3490 | | } |
| 98 | 3491 | | } |
| | 3492 | |
|
| 104 | 3493 | | if (IsUnaryLeftOperator()) |
| 6 | 3494 | | { |
| 6 | 3495 | | if (@operator is null || priority > ParsableLeftUnaryOperators![currentMatch.Value].Item1) |
| 5 | 3496 | | { |
| 5 | 3497 | | @operator = currentMatch; |
| 5 | 3498 | | isUnaryLeftOperator = true; |
| 5 | 3499 | | isUnaryRightOperator = false; |
| 5 | 3500 | | isBinaryOperator = false; |
| 5 | 3501 | | priority = ParsableLeftUnaryOperators![currentMatch.Value].Item1; |
| 5 | 3502 | | } |
| 6 | 3503 | | } |
| 98 | 3504 | | else if (IsUnaryRightOperator()) |
| 14 | 3505 | | { |
| 14 | 3506 | | if (@operator is null || priority > ParsableRightUnaryOperators![currentMatch.Value].Item1) |
| 14 | 3507 | | { |
| 14 | 3508 | | @operator = currentMatch; |
| 14 | 3509 | | isUnaryLeftOperator = false; |
| 14 | 3510 | | isUnaryRightOperator = true; |
| 14 | 3511 | | isBinaryOperator = false; |
| 14 | 3512 | | priority = ParsableRightUnaryOperators![currentMatch.Value].Item1; |
| 14 | 3513 | | } |
| 14 | 3514 | | } |
| | 3515 | | else |
| 84 | 3516 | | { |
| 84 | 3517 | | if (ParsableBinaryOperators!.ContainsKey(currentMatch.Value)) |
| 80 | 3518 | | { |
| | 3519 | | // Binary Operator |
| 80 | 3520 | | if (@operator is null || priority > ParsableBinaryOperators[currentMatch.Value].Item1) |
| 74 | 3521 | | { |
| 74 | 3522 | | @operator = currentMatch; |
| 74 | 3523 | | isUnaryLeftOperator = false; |
| 74 | 3524 | | isUnaryRightOperator = false; |
| 74 | 3525 | | isBinaryOperator = true; |
| 74 | 3526 | | priority = ParsableBinaryOperators[currentMatch.Value].Item1; |
| 74 | 3527 | | } |
| 80 | 3528 | | } |
| 84 | 3529 | | } |
| 104 | 3530 | | } |
| 136 | 3531 | | currentOperatorMatch++; |
| | 3532 | |
|
| 136 | 3533 | | if (currentOperatorMatch >= operatorMatches.Count) |
| 98 | 3534 | | { |
| 98 | 3535 | | break; |
| | 3536 | | } |
| 38 | 3537 | | } |
| 349 | 3538 | | } |
| | 3539 | |
|
| | 3540 | | // if an operator was found, parse the expression |
| 98 | 3541 | | if (@operator is not null) |
| 86 | 3542 | | { |
| 86 | 3543 | | if (isUnaryLeftOperator) |
| 4 | 3544 | | { |
| 4 | 3545 | | string a = @string[(@operator.Index + @operator.Length)..]; |
| 4 | 3546 | | Expression A = Parse(a, tryParse); |
| 4 | 3547 | | expression = ParsableLeftUnaryOperators![@operator.Value].Item2(A); |
| 4 | 3548 | | return true; |
| | 3549 | | } |
| 82 | 3550 | | else if (isUnaryRightOperator) |
| 10 | 3551 | | { |
| 10 | 3552 | | string a = @string[..@operator.Index]; |
| 10 | 3553 | | Expression A = Parse(a, tryParse); |
| 10 | 3554 | | expression = ParsableRightUnaryOperators![@operator.Value].Item2(A); |
| 10 | 3555 | | return true; |
| | 3556 | | } |
| 72 | 3557 | | else if (isBinaryOperator) |
| 72 | 3558 | | { |
| 72 | 3559 | | string a = @string[..@operator.Index]; |
| 72 | 3560 | | Expression A = Parse(a, tryParse); |
| 72 | 3561 | | string b = @string[(@operator.Index + @operator.Length)..]; |
| 72 | 3562 | | Expression B = Parse(b, tryParse); |
| 72 | 3563 | | expression = ParsableBinaryOperators![@operator.Value].Item2(A, B); |
| 72 | 3564 | | return true; |
| | 3565 | | } |
| 0 | 3566 | | } |
| 12 | 3567 | | } |
| | 3568 | |
|
| | 3569 | | // No non-nested operator patterns found. Fall back. |
| 244 | 3570 | | expression = null; |
| 244 | 3571 | | return false; |
| 330 | 3572 | | } |
| | 3573 | |
|
| | 3574 | | internal static bool TryParseParenthesisExpression<T>(string @string, Func<string, (bool Success, T? Value)> tryParse, |
| 244 | 3575 | | { |
| | 3576 | | // Try to match a parenthesis pattern. |
| 244 | 3577 | | Match parenthesisMatch = Regex.Match(@string, ParenthesisPattern); |
| 244 | 3578 | | Match operationMatch = Regex.Match(@string, ParsableOperationsRegexPattern!); |
| 244 | 3579 | | if (parenthesisMatch.Success) |
| 12 | 3580 | | { |
| 12 | 3581 | | if (operationMatch.Success && parenthesisMatch.Index > operationMatch.Index) |
| 0 | 3582 | | { |
| | 3583 | | // The next set of parenthesis are part of an operation. Fall back and |
| | 3584 | | // let the TryParseOperationExpression handle it. |
| 0 | 3585 | | expression = null; |
| 0 | 3586 | | return false; |
| | 3587 | | } |
| | 3588 | |
|
| | 3589 | | // Parse the nested expression |
| 12 | 3590 | | string nestedExpression = parenthesisMatch.Value.Substring(1, parenthesisMatch.Length - 2); |
| 12 | 3591 | | expression = Parse(nestedExpression, tryParse); |
| | 3592 | |
|
| | 3593 | | // Check for implicit multiplications to the left of the parenthesis pattern |
| 12 | 3594 | | if (parenthesisMatch.Index > 0) |
| 0 | 3595 | | { |
| 0 | 3596 | | string leftExpression = @string[..parenthesisMatch.Index]; |
| 0 | 3597 | | expression *= Parse(leftExpression, tryParse); |
| 0 | 3598 | | } |
| | 3599 | |
|
| | 3600 | | // Check for implicit multiplications to the right of the parenthesis pattern |
| 12 | 3601 | | int right_start = parenthesisMatch.Index + parenthesisMatch.Length; |
| 12 | 3602 | | if (right_start != @string.Length) |
| 0 | 3603 | | { |
| 0 | 3604 | | string rightExpression = @string[right_start..]; |
| 0 | 3605 | | expression *= Parse(rightExpression, tryParse); |
| 0 | 3606 | | } |
| | 3607 | |
|
| | 3608 | | // Parsing was successful |
| 12 | 3609 | | return true; |
| | 3610 | | } |
| | 3611 | |
|
| | 3612 | | // No parenthesis pattern found. Fall back. |
| 232 | 3613 | | expression = null; |
| 232 | 3614 | | return false; |
| 244 | 3615 | | } |
| | 3616 | |
|
| | 3617 | | internal static bool TryParseOperationExpression<T>(string @string, Func<string, (bool Success, T? Value)> tryParse, o |
| 232 | 3618 | | { |
| 232 | 3619 | | expression = null; |
| 232 | 3620 | | Match operationMatch = Regex.Match(@string, ParsableOperationsRegexPattern!); |
| | 3621 | |
|
| 232 | 3622 | | if (operationMatch.Success) |
| 0 | 3623 | | { |
| 0 | 3624 | | string operationMatch_Value = operationMatch.Value; |
| 0 | 3625 | | string operation = operationMatch_Value[..operationMatch_Value.IndexOf('(')]; |
| 0 | 3626 | | Match parenthesisMatch = Regex.Match(@string, ParenthesisPattern); |
| 0 | 3627 | | string parenthesisMatch_Value = parenthesisMatch.Value; |
| 0 | 3628 | | ListArray<string> operandSplits = SplitOperands(parenthesisMatch_Value[1..^1]); |
| | 3629 | |
|
| 0 | 3630 | | switch (operandSplits.Count) |
| | 3631 | | { |
| | 3632 | | case 1: |
| 0 | 3633 | | if (ParsableUnaryOperations!.TryGetValue(operation, out Func<Expression, Unary>? newUnaryFunction)) |
| 0 | 3634 | | { |
| 0 | 3635 | | expression = newUnaryFunction(Parse<T>(operandSplits[0])); |
| 0 | 3636 | | } |
| 0 | 3637 | | break; |
| | 3638 | | case 2: |
| 0 | 3639 | | if (ParsableBinaryOperations!.TryGetValue(operation, out Func<Expression, Expression, Binary>? newBinaryFuncti |
| 0 | 3640 | | { |
| 0 | 3641 | | expression = newBinaryFunction(Parse<T>(operandSplits[0]), Parse<T>(operandSplits[1])); |
| 0 | 3642 | | } |
| 0 | 3643 | | break; |
| | 3644 | | case 3: |
| 0 | 3645 | | if (ParsableTernaryOperations!.TryGetValue(operation, out Func<Expression, Expression, Expression, Ternary>? n |
| 0 | 3646 | | { |
| 0 | 3647 | | expression = newTernaryFunction(Parse<T>(operandSplits[0]), Parse<T>(operandSplits[2]), Parse<T>(operandSpli |
| 0 | 3648 | | } |
| 0 | 3649 | | break; |
| | 3650 | | } |
| 0 | 3651 | | if (ParsableMultinaryOperations!.TryGetValue(operation, out Func<Expression[], Multinary>? newMultinaryFunction)) |
| 0 | 3652 | | { |
| 0 | 3653 | | expression = newMultinaryFunction(operandSplits.Select(x => Parse<T>(x)).ToArray()); |
| 0 | 3654 | | } |
| 0 | 3655 | | if (expression is null) |
| 0 | 3656 | | { |
| 0 | 3657 | | throw new ArgumentException("The expression could not be parsed. { " + @string + " }", nameof(@string)); |
| | 3658 | | } |
| | 3659 | | // handle implicit multiplications if any exist |
| 0 | 3660 | | if (operationMatch.Index != 0) // Left |
| 0 | 3661 | | { |
| 0 | 3662 | | Expression A = Parse(@string[..operationMatch.Index], tryParse); |
| 0 | 3663 | | expression *= A; |
| 0 | 3664 | | } |
| 0 | 3665 | | if (operationMatch.Length + operationMatch.Index < @string.Length) // Right |
| 0 | 3666 | | { |
| 0 | 3667 | | Expression A = Parse(@string[(operationMatch.Length + operationMatch.Index)..], tryParse); |
| 0 | 3668 | | expression *= A; |
| 0 | 3669 | | } |
| 0 | 3670 | | return true; |
| | 3671 | | } |
| | 3672 | |
|
| | 3673 | | // No operation pattern found. Fall back. |
| 232 | 3674 | | return false; |
| 232 | 3675 | | } |
| | 3676 | |
|
| | 3677 | | internal static ListArray<string> SplitOperands(string @string) |
| 0 | 3678 | | { |
| 0 | 3679 | | ListArray<string> operands = new(); |
| 0 | 3680 | | int scope = 0; |
| 0 | 3681 | | int operandStart = 0; |
| 0 | 3682 | | for (int i = 0; i < @string.Length; i++) |
| 0 | 3683 | | { |
| 0 | 3684 | | switch (@string[i]) |
| | 3685 | | { |
| 0 | 3686 | | case '(': scope++; break; |
| 0 | 3687 | | case ')': scope--; break; |
| | 3688 | | case ',': |
| 0 | 3689 | | if (scope is 0) |
| 0 | 3690 | | { |
| 0 | 3691 | | operands.Add(@string[operandStart..i]); |
| 0 | 3692 | | } |
| 0 | 3693 | | break; |
| | 3694 | | } |
| 0 | 3695 | | } |
| 0 | 3696 | | if (scope != 0) |
| 0 | 3697 | | { |
| 0 | 3698 | | throw new ArgumentException("The expression could not be parsed. { " + @string + " }", nameof(@string)); |
| | 3699 | | } |
| 0 | 3700 | | operands.Add(@string[operandStart..]); |
| 0 | 3701 | | return operands; |
| 0 | 3702 | | } |
| | 3703 | |
|
| | 3704 | | internal static bool TryParseVariablesExpression<T>(string @string, Func<string, (bool Success, T? Value)> tryParse, o |
| 232 | 3705 | | { |
| 232 | 3706 | | string variablePattern = @"\[.*\]"; |
| | 3707 | |
|
| | 3708 | | // extract and parse variables |
| 232 | 3709 | | System.Collections.Generic.IEnumerable<Expression> variables = |
| 232 | 3710 | | Regex.Matches(@string, variablePattern) |
| 232 | 3711 | | .Cast<Match>() |
| 232 | 3712 | | .Select(x => new Variable(x.Value[1..^1])); |
| | 3713 | |
|
| | 3714 | | // if no variables, fall back |
| 232 | 3715 | | if (!variables.Any()) |
| 232 | 3716 | | { |
| 232 | 3717 | | parsedExpression = null; |
| 232 | 3718 | | return false; |
| | 3719 | | } |
| | 3720 | |
|
| | 3721 | | // assume the remaining string splits are constants and try to parse them |
| 0 | 3722 | | System.Collections.Generic.IEnumerable<Expression?> constants = |
| 0 | 3723 | | Regex.Split(@string, variablePattern) |
| 0 | 3724 | | .Where(x => !string.IsNullOrWhiteSpace(x)) |
| 0 | 3725 | | .Select(x => |
| 0 | 3726 | | { |
| 0 | 3727 | | TryParseConstantExpression(x, tryParse, out var exp); |
| 0 | 3728 | | return exp; |
| 0 | 3729 | | }); |
| | 3730 | |
|
| | 3731 | | // multiply all the expressions together, starting with the constants because |
| | 3732 | | // it will look better if converted to a string |
| 0 | 3733 | | bool set = false; |
| 0 | 3734 | | parsedExpression = null; |
| 0 | 3735 | | foreach (var constant in constants.Concat(variables)) |
| 0 | 3736 | | { |
| 0 | 3737 | | if (!set) |
| 0 | 3738 | | { |
| 0 | 3739 | | parsedExpression = constant; |
| 0 | 3740 | | set = true; |
| 0 | 3741 | | } |
| | 3742 | | else |
| 0 | 3743 | | { |
| 0 | 3744 | | if (parsedExpression is null) |
| 0 | 3745 | | { |
| 0 | 3746 | | throw new TowelBugException($"Encountered null {nameof(parsedExpression)} in {nameof(TryParseVariablesExpressi |
| | 3747 | | } |
| 0 | 3748 | | if (constant is null) |
| 0 | 3749 | | { |
| 0 | 3750 | | throw new TowelBugException($"Encountered null {nameof(constant)} in {nameof(TryParseVariablesExpression)}."); |
| | 3751 | | } |
| 0 | 3752 | | parsedExpression *= constant; |
| 0 | 3753 | | } |
| 0 | 3754 | | } |
| 0 | 3755 | | return true; |
| 232 | 3756 | | } |
| | 3757 | |
|
| | 3758 | | internal static bool TryParseKnownConstantExpression<T>(string @string, Func<string, (bool Success, T? Value)> tryPars |
| 232 | 3759 | | { |
| 232 | 3760 | | Match knownConstantMatch = Regex.Match(@string, ParsableKnownConstantsRegexPattern!); |
| | 3761 | |
|
| 232 | 3762 | | if (knownConstantMatch.Success) |
| 18 | 3763 | | { |
| 18 | 3764 | | parsedExpression = ParsableKnownConstants![knownConstantMatch.Value]().ApplyType<T>(); |
| | 3765 | |
|
| | 3766 | | // implied multiplications to the left and right |
| 18 | 3767 | | if (knownConstantMatch.Index != 0) |
| 6 | 3768 | | { |
| 6 | 3769 | | Expression A = Parse<T>(@string[..knownConstantMatch.Index], tryParse); |
| 6 | 3770 | | parsedExpression *= A; |
| 6 | 3771 | | } |
| 18 | 3772 | | if (knownConstantMatch.Index < @string.Length - 1) |
| 0 | 3773 | | { |
| 0 | 3774 | | Expression B = Parse<T>(@string[(knownConstantMatch.Index + 1)..], tryParse); |
| 0 | 3775 | | parsedExpression *= B; |
| 0 | 3776 | | } |
| 18 | 3777 | | return true; |
| | 3778 | | } |
| | 3779 | |
|
| 214 | 3780 | | parsedExpression = null; |
| 214 | 3781 | | return false; |
| 232 | 3782 | | } |
| | 3783 | |
|
| | 3784 | | internal static bool TryParseConstantExpression<T>(string @string, Func<string, (bool Success, T? Value)> tryParse, ou |
| 214 | 3785 | | { |
| 214 | 3786 | | var (parseSuccess, parseValue) = tryParse(@string); |
| 214 | 3787 | | if (parseSuccess) |
| 214 | 3788 | | { |
| 214 | 3789 | | parsedExpression = new Constant<T?>(parseValue); |
| 214 | 3790 | | return true; |
| | 3791 | | } |
| 0 | 3792 | | int decimalIndex = -1; |
| 0 | 3793 | | for (int i = 0; i < @string.Length; i++) |
| 0 | 3794 | | { |
| 0 | 3795 | | char character = @string[i]; |
| 0 | 3796 | | if (character == '.') |
| 0 | 3797 | | { |
| 0 | 3798 | | if (decimalIndex >= 0 || i == @string.Length - 1) |
| 0 | 3799 | | { |
| 0 | 3800 | | parsedExpression = null; |
| 0 | 3801 | | return false; |
| | 3802 | | } |
| 0 | 3803 | | decimalIndex = i; |
| 0 | 3804 | | } |
| 0 | 3805 | | if ('0' > character && character > '9') |
| 0 | 3806 | | { |
| 0 | 3807 | | parsedExpression = null; |
| 0 | 3808 | | return false; |
| | 3809 | | } |
| 0 | 3810 | | } |
| 0 | 3811 | | if (decimalIndex >= 0) |
| 0 | 3812 | | { |
| | 3813 | | string wholeNumberString; |
| 0 | 3814 | | if (decimalIndex is 0) |
| 0 | 3815 | | { |
| 0 | 3816 | | wholeNumberString = "0"; |
| 0 | 3817 | | } |
| | 3818 | | else |
| 0 | 3819 | | { |
| 0 | 3820 | | wholeNumberString = @string[..decimalIndex]; |
| 0 | 3821 | | } |
| 0 | 3822 | | string decimalPlacesString = @string[(decimalIndex + 1)..]; |
| | 3823 | |
|
| 0 | 3824 | | int zeroCount = 0; |
| 0 | 3825 | | while (decimalPlacesString[zeroCount] == '0') |
| 0 | 3826 | | { |
| 0 | 3827 | | zeroCount++; |
| 0 | 3828 | | } |
| | 3829 | |
|
| 0 | 3830 | | if (int.TryParse(wholeNumberString, out int wholeNumberInt) && |
| 0 | 3831 | | int.TryParse(decimalPlacesString, out int decimalPlacesInt)) |
| 0 | 3832 | | { |
| 0 | 3833 | | T wholeNumber = Convert<int, T>(wholeNumberInt); |
| 0 | 3834 | | T decimalPlaces = Convert<int, T>(decimalPlacesInt); |
| 0 | 3835 | | while (GreaterThanOrEqual(decimalPlaces, Towel.Constant<T>.One)) |
| 0 | 3836 | | { |
| 0 | 3837 | | decimalPlaces = Division(decimalPlaces, Towel.Constant<T>.Ten); |
| 0 | 3838 | | } |
| 0 | 3839 | | for (; zeroCount > 0; zeroCount--) |
| 0 | 3840 | | { |
| 0 | 3841 | | decimalPlaces = Division(decimalPlaces, Towel.Constant<T>.Ten); |
| 0 | 3842 | | } |
| 0 | 3843 | | parsedExpression = new Constant<T>(Addition(wholeNumber, decimalPlaces)); |
| 0 | 3844 | | return true; |
| | 3845 | | } |
| 0 | 3846 | | } |
| | 3847 | | else |
| 0 | 3848 | | { |
| 0 | 3849 | | if (int.TryParse(@string, out int parsedInt)) |
| 0 | 3850 | | { |
| 0 | 3851 | | parsedExpression = new Constant<T>(Convert<int, T>(parsedInt)); |
| 0 | 3852 | | return true; |
| | 3853 | | } |
| 0 | 3854 | | } |
| 0 | 3855 | | parsedExpression = null; |
| 0 | 3856 | | return false; |
| 214 | 3857 | | } |
| | 3858 | |
|
| | 3859 | | #endregion |
| | 3860 | |
|
| | 3861 | | #endregion |
| | 3862 | | } |