mirror of
https://github.com/0xJ1M/MathsEngine.git
synced 2026-06-05 01:50:07 +00:00
WIP: Implemented a rough system for function evaluation
This commit is contained in:
@@ -4,6 +4,7 @@ using MathEngine.AST.Nodes;
|
||||
using MathEngine.Tokenizer;
|
||||
|
||||
using static MathEngine.Tokenizer.Token;
|
||||
using MathEngine.Types;
|
||||
|
||||
namespace EngineTests.Parser_Tests.Nodes
|
||||
{
|
||||
@@ -18,8 +19,8 @@ namespace EngineTests.Parser_Tests.Nodes
|
||||
[Fact]
|
||||
public void TestNodeFactoryBinaryNodesOnDefinedOperations()
|
||||
{
|
||||
NumericNode<decimal> node1 = new(200);
|
||||
NumericNode<decimal> node2 = new(100);
|
||||
NumericNode node1 = new(new DecimalValue(200));
|
||||
NumericNode node2 = new(new DecimalValue(100));
|
||||
|
||||
Token plus = Token.Plus;
|
||||
Token minus = Token.Minus;
|
||||
|
||||
@@ -1,11 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MathEngine.AST.Nodes
|
||||
namespace MathEngine.AST.Nodes
|
||||
{
|
||||
/// <summary>
|
||||
/// Abstract class representing a Node in a Tree structure
|
||||
|
||||
@@ -1,21 +1,23 @@
|
||||
using System;
|
||||
using MathEngine.Types.Interfaces;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using static MathEngine.Functions.BuiltIns.BuiltIns;
|
||||
|
||||
namespace MathEngine.AST.Nodes
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a Tree node that stores a function
|
||||
/// </summary>
|
||||
internal class FunctionNode<T> : BaseNode where T : INumber<T>
|
||||
internal class FunctionNode : BaseNode
|
||||
{
|
||||
Action func;
|
||||
List<NumericNode<T>> args;
|
||||
private FunctionWrapper func;
|
||||
private INumericNode[] args;
|
||||
|
||||
public FunctionNode(Action Function, List<NumericNode<T>> arguments)
|
||||
public FunctionNode(FunctionWrapper Function, INumericNode[] arguments)
|
||||
{
|
||||
func = Function;
|
||||
args = arguments;
|
||||
@@ -23,7 +25,7 @@ namespace MathEngine.AST.Nodes
|
||||
|
||||
public override BaseNode Evaluate()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
return func(args);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
using MathEngine.Tokenizer;
|
||||
|
||||
using MathEngine.Functions.BuiltIns;
|
||||
using MathEngine.Tokenizer;
|
||||
using MathEngine.Types;
|
||||
using MathEngine.Types.Interfaces;
|
||||
using System.Runtime.Intrinsics.X86;
|
||||
using Xunit.Sdk;
|
||||
using static MathEngine.Tokenizer.Token;
|
||||
|
||||
namespace MathEngine.AST.Nodes
|
||||
@@ -32,7 +36,7 @@ namespace MathEngine.AST.Nodes
|
||||
return new BinaryNode(LeftBranch, RightBranch, (a, b) => a / b);
|
||||
case TokenType.Exponentiation:
|
||||
throw new NotImplementedException("Exponentiation is not supported at this time!");
|
||||
return new BinaryNode(LeftBranch, RightBranch, (a, b) => a ^ b);
|
||||
// return new BinaryNode(LeftBranch, RightBranch, (a, b) => a ^ b);
|
||||
default:
|
||||
throw new NotImplementedException("Attempted to create a BinaryNode with an invalid operation!");
|
||||
}
|
||||
@@ -46,23 +50,25 @@ namespace MathEngine.AST.Nodes
|
||||
/// <returns></returns>
|
||||
public static BaseNode CreateFunctionNode(Token FunctionToken, Stack<BaseNode> ArgumentStack)
|
||||
{
|
||||
throw new NotImplementedException("Not implemented at this time");
|
||||
/* Check that the function exists, if it does we then check that the number of arguments
|
||||
* for the function is are avaliable on the ArgumentStack. If so we can construct the Node.
|
||||
* Otherwise, either the function does not exist or there is an argument mismatch */
|
||||
/*try
|
||||
{
|
||||
functionAction = GetFunction(FunctionToken, out int functionArity);
|
||||
if (functionArity >= ArgumentStack.Count)
|
||||
{
|
||||
throw new ArgumentException("Number of required arguments is greater than avaliable arugments");
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
int argc = (int)FunctionToken.Arity;
|
||||
|
||||
throw;
|
||||
}*/
|
||||
INumericNode[] argsv = new INumericNode[argc];
|
||||
|
||||
for (int i = argc; i > 0; i--)
|
||||
{
|
||||
// TODO: Update so either INumeric or function nodes are allowed
|
||||
// this will enable nested functions
|
||||
if (ArgumentStack.Peek() is INumericNode numericNode)
|
||||
{
|
||||
argsv[i - 1] = numericNode;
|
||||
ArgumentStack.Pop();
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException("Function arguments implement INumericNode.");
|
||||
}
|
||||
}
|
||||
return new FunctionNode(BuiltIns.GetFunction(FunctionToken.Value), argsv);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -81,7 +87,7 @@ namespace MathEngine.AST.Nodes
|
||||
{
|
||||
throw new ArgumentException("Failure to parse number");
|
||||
}
|
||||
return new NumericNode<decimal>(res);
|
||||
return new NumericallyOrderableNode<DecimalValue>(new DecimalValue(res));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,87 +1,115 @@
|
||||
using System.Numerics;
|
||||
using MathEngine.Types.Interfaces;
|
||||
using System.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace MathEngine.AST.Nodes
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a Tree node that can store a numeric value
|
||||
/// </summary>
|
||||
internal class NumericNode<T> : BaseNode where T : INumber<T>
|
||||
internal class NumericNode<T>: BaseNode, INumericNode, ITrigonometricNode where T : struct, INumeric<T>
|
||||
{
|
||||
private readonly decimal Value;
|
||||
protected readonly T _value;
|
||||
|
||||
Type INumericNode.NumericType
|
||||
{
|
||||
get
|
||||
{
|
||||
return typeof(T);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initialises a new instance of a NumericNode with a given Token
|
||||
/// </summary>
|
||||
/// <param name="value">The token for the nodes value</param>
|
||||
public NumericNode(decimal value)
|
||||
public NumericNode(T value)
|
||||
{
|
||||
Children = null;
|
||||
Value = value;
|
||||
_value = value;
|
||||
}
|
||||
|
||||
protected override BaseNode Add(BaseNode otherNode)
|
||||
{
|
||||
if (otherNode is NumericNode<decimal>)
|
||||
if (otherNode is NumericNode<T> rhs)
|
||||
{
|
||||
NumericNode<decimal> rhs = (NumericNode<decimal>)otherNode;
|
||||
return new NumericNode<decimal>(Value + rhs.Value);
|
||||
return new NumericNode<T>(_value.Add(rhs._value));
|
||||
}
|
||||
throw new InvalidOperationException("Attempted Invalid operation");
|
||||
}
|
||||
|
||||
protected override BaseNode Subtract(BaseNode otherNode)
|
||||
{
|
||||
if (otherNode is NumericNode<decimal>)
|
||||
if (otherNode is NumericNode<T>)
|
||||
{
|
||||
NumericNode<decimal> rhs = (NumericNode<decimal>)otherNode;
|
||||
return new NumericNode<decimal>(Value - rhs.Value);
|
||||
NumericNode<T> rhs = (NumericNode<T>)otherNode;
|
||||
return new NumericNode<T>(_value.Subtract(rhs._value));
|
||||
}
|
||||
throw new InvalidOperationException("Attempted Invalid operation");
|
||||
}
|
||||
|
||||
protected override BaseNode Multiply(BaseNode otherNode)
|
||||
{
|
||||
if (otherNode is NumericNode<decimal>)
|
||||
if (otherNode is NumericNode<T>)
|
||||
{
|
||||
NumericNode<decimal> rhs = (NumericNode<decimal>)otherNode;
|
||||
return new NumericNode<decimal>(Value * rhs.Value);
|
||||
NumericNode<T> rhs = (NumericNode<T>)otherNode;
|
||||
return new NumericNode<T>(_value.Multiply(rhs._value));
|
||||
}
|
||||
throw new InvalidOperationException("Attempted Invalid operation");
|
||||
}
|
||||
|
||||
protected override BaseNode Divide(BaseNode otherNode)
|
||||
{
|
||||
if (otherNode is NumericNode<decimal>)
|
||||
if (otherNode is NumericNode<T>)
|
||||
{
|
||||
NumericNode<decimal> rhs = (NumericNode<decimal>)otherNode;
|
||||
return new NumericNode<decimal>(Value / rhs.Value);
|
||||
NumericNode<T> rhs = (NumericNode<T>)otherNode;
|
||||
return new NumericNode<T>(_value.Divide(rhs._value));
|
||||
}
|
||||
throw new InvalidOperationException("Attempted Invalid operation");
|
||||
}
|
||||
|
||||
protected override BaseNode Exponentiate(BaseNode otherNode)
|
||||
{
|
||||
if (otherNode is NumericNode<decimal>)
|
||||
{
|
||||
NumericNode<decimal> rhs = (NumericNode<decimal>)otherNode;
|
||||
return new NumericNode<decimal>((decimal)Math.Pow((double)Value, (double)rhs.Value));
|
||||
}
|
||||
throw new InvalidOperationException("Attempted Invalid operation");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Evaluates the Numeric Node by simply returning the current instance
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public override BaseNode Evaluate()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
internal T GetValue()
|
||||
{
|
||||
return Value.ToString();
|
||||
return _value;
|
||||
}
|
||||
|
||||
void INumericNode.CopyTo<TDest>(ref TDest destination)
|
||||
{
|
||||
if (typeof(TDest) == typeof(T))
|
||||
{
|
||||
destination = Unsafe.As<T, TDest>(ref Unsafe.AsRef(in _value));
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException($"Cannot copy {typeof(T)} to {typeof(TDest)}");
|
||||
}
|
||||
}
|
||||
|
||||
public INumericNode Sin()
|
||||
{
|
||||
if (_value is ITrigonometric<T> trig)
|
||||
return new NumericNode<T>(trig.Sin());
|
||||
throw new NotImplementedException();
|
||||
|
||||
}
|
||||
|
||||
public INumericNode Cos()
|
||||
{
|
||||
if (_value is ITrigonometric<T> trig)
|
||||
return new NumericNode<T>(trig.Cos());
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public INumericNode Tan()
|
||||
{
|
||||
if (_value is ITrigonometric<T> trig)
|
||||
return new NumericNode<T>(trig.Tan());
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
55
MathEngine/MathEngine/AST/Nodes/NumericallyOrderableNode.cs
Normal file
55
MathEngine/MathEngine/AST/Nodes/NumericallyOrderableNode.cs
Normal file
@@ -0,0 +1,55 @@
|
||||
using MathEngine.Types.Interfaces;
|
||||
|
||||
namespace MathEngine.AST.Nodes
|
||||
{
|
||||
internal class NumericallyOrderableNode<T> : NumericNode<T>, INumericallyOrderable where T : struct, INumericallyOrderable<T>
|
||||
{
|
||||
public NumericallyOrderableNode(T value) : base(value)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
int INumericallyOrderable.CompareTo(INumericNode other)
|
||||
{
|
||||
if (other is NumericallyOrderableNode<T> rhs)
|
||||
{
|
||||
return this._value.CompareTo(rhs._value);
|
||||
}
|
||||
throw new Exception();
|
||||
}
|
||||
|
||||
bool INumericallyOrderable.GreaterThan(INumericNode other)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
bool INumericallyOrderable.GreaterThanOrEqual(INumericNode other)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
bool INumericallyOrderable.LessThan(INumericNode other)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
bool INumericallyOrderable.LessThanOrEqual(INumericNode other)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
INumericallyOrderable INumericallyOrderable.Max(INumericNode other)
|
||||
{
|
||||
if (other is NumericallyOrderableNode<T> rhs)
|
||||
return _value.GreaterThanOrEqual(rhs._value) ? this : rhs;
|
||||
|
||||
throw new InvalidOperationException("Cannot compare incompatible numeric node types");
|
||||
|
||||
}
|
||||
|
||||
INumericallyOrderable INumericallyOrderable.Min(INumericNode other)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
using MathEngine.AST;
|
||||
using MathEngine.AST.Nodes;
|
||||
using MathEngine.Types;
|
||||
|
||||
namespace MathEngine.Expression
|
||||
{
|
||||
|
||||
105
MathEngine/MathEngine/Functions/BuiltIns/BuiltIns.cs
Normal file
105
MathEngine/MathEngine/Functions/BuiltIns/BuiltIns.cs
Normal file
@@ -0,0 +1,105 @@
|
||||
using MathEngine.AST.Nodes;
|
||||
using MathEngine.Types;
|
||||
using MathEngine.Types.Interfaces;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using static System.Runtime.InteropServices.JavaScript.JSType;
|
||||
|
||||
namespace MathEngine.Functions.BuiltIns
|
||||
{
|
||||
internal class BuiltIns
|
||||
{
|
||||
// Uniform wrapper type: takes decimal[] and returns result via callback
|
||||
public delegate BaseNode FunctionWrapper(INumericNode[] args);
|
||||
|
||||
// Dictionary storing the wrapped functions
|
||||
private static readonly Dictionary<string, FunctionWrapper> _builtIns = new()
|
||||
{
|
||||
{ "sin", Wrap(SinImply)},
|
||||
{ "cos", Wrap(CosImply)},
|
||||
{ "tan", Wrap(TanImply)},
|
||||
{ "max", Wrap(MaxImply)},
|
||||
};
|
||||
|
||||
|
||||
|
||||
// Wrap a Func<decimal[], decimal> into FunctionWrapper
|
||||
private static FunctionWrapper Wrap(Func<INumericNode[], INumericNode> func)
|
||||
{
|
||||
return args => (BaseNode)func(args);
|
||||
}
|
||||
|
||||
|
||||
public static bool IsFunction(string name)
|
||||
{
|
||||
return _builtIns.ContainsKey(name);
|
||||
}
|
||||
|
||||
// Retrieve the wrapper for a function
|
||||
public static FunctionWrapper GetFunction(string name)
|
||||
{
|
||||
if (!_builtIns.TryGetValue(name, out var func))
|
||||
throw new Exception($"Unknown function: {name}");
|
||||
return func;
|
||||
}
|
||||
|
||||
|
||||
private static INumericNode SinImply(INumericNode[] span)
|
||||
{
|
||||
INumericNode arg = span[0];
|
||||
if (arg is ITrigonometricNode trigNode)
|
||||
{
|
||||
return trigNode.Sin();
|
||||
}
|
||||
|
||||
throw new InvalidOperationException("Argument does not support Sin()");
|
||||
}
|
||||
|
||||
private static INumericNode CosImply(INumericNode[] span)
|
||||
{
|
||||
INumericNode arg = span[0];
|
||||
if (arg is ITrigonometricNode trigNode)
|
||||
{
|
||||
return trigNode.Cos();
|
||||
}
|
||||
|
||||
throw new InvalidOperationException("Argument does not support Sin()");
|
||||
}
|
||||
|
||||
private static INumericNode TanImply(INumericNode[] span)
|
||||
{
|
||||
INumericNode arg = span[0];
|
||||
if (arg is ITrigonometricNode trigNode)
|
||||
{
|
||||
return trigNode.Tan();
|
||||
}
|
||||
|
||||
throw new InvalidOperationException("Argument does not support Sin()");
|
||||
}
|
||||
private static INumericNode MaxImply(INumericNode[] span)
|
||||
{
|
||||
var maxNode = span[0] as INumericallyOrderable
|
||||
?? throw new InvalidOperationException("Argument not orderable");
|
||||
|
||||
foreach (var node in span.Skip(1))
|
||||
{
|
||||
if (node is INumericallyOrderable orderable)
|
||||
{
|
||||
maxNode = maxNode.Max(orderable);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException("All arguments must be orderable");
|
||||
}
|
||||
}
|
||||
|
||||
return maxNode;
|
||||
}
|
||||
|
||||
////// Allow registering user-defined functions
|
||||
////public static void RegisterFunction(string name, Func<ReadOnlySpan<INumericNode>, INumericNode> func)
|
||||
////{
|
||||
//// _builtIns[name] = Wrap(func);
|
||||
////}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using MathEngine.Tokenizer;
|
||||
using MathEngine.Functions.BuiltIns;
|
||||
using MathEngine.Tokenizer;
|
||||
using System.Security;
|
||||
using static MathEngine.Tokenizer.Token;
|
||||
|
||||
@@ -155,8 +156,10 @@ namespace MathEngine.Parser
|
||||
Stack<Token> OperatorStack = new(Expression.Count/2);
|
||||
//The final stack to return
|
||||
Queue<Token> rpnQueue = new(Expression.Count);
|
||||
//Stack used to hold the number of input params to a function
|
||||
//Stack<uint> ArityStack = new Stack<uint>();
|
||||
|
||||
// Stack that is used to handle nested function argument counts
|
||||
Stack<UInt32> arityStack = new();
|
||||
|
||||
Token PreviousToken = Token.None;
|
||||
Token CurrentToken;
|
||||
|
||||
@@ -179,8 +182,33 @@ namespace MathEngine.Parser
|
||||
PopOperatorsToQueue(OperatorStack, rpnQueue, CurrentToken);
|
||||
OperatorStack.Push(CurrentToken);
|
||||
break;
|
||||
case TokenType.Function:
|
||||
OperatorStack.Push(CurrentToken);
|
||||
case TokenType.GenericIdentifier:
|
||||
// few things to consider
|
||||
// 1 Check built-ins
|
||||
if (i + 1 < Expression.Count && Expression[i + 1].Type == TokenType.LeftParenthesis && BuiltIns.IsFunction(CurrentToken.Value))
|
||||
{
|
||||
OperatorStack.Push(CurrentToken with { Type = Token.TokenType.Function });
|
||||
arityStack.Push(1); // at least one arg must be present
|
||||
break;
|
||||
}
|
||||
// 2 check user defined functions
|
||||
// TODO: Implement User definable functions
|
||||
|
||||
// 3 check to see if it is a user defined variable
|
||||
// TODO: Implement user definable variables
|
||||
|
||||
// 4 assume it is an implict multiplication
|
||||
// TODO: Split identifier into letters and insert implicit multiplication
|
||||
break;
|
||||
case TokenType.FunctionArgumentSeparator:
|
||||
if (arityStack.Count == 0)
|
||||
throw new ParserException("Unexpected function arguement separator");
|
||||
// Pop operators until the last LeftParenthesis
|
||||
while (OperatorStack.Count > 0 && OperatorStack.Peek().Type != TokenType.LeftParenthesis)
|
||||
rpnQueue.Enqueue(OperatorStack.Pop());
|
||||
|
||||
// increment the argument counter
|
||||
arityStack.Push(arityStack.Pop() + 1);
|
||||
break;
|
||||
case TokenType.LeftParenthesis:
|
||||
OperatorStack.Push(CurrentToken);
|
||||
@@ -198,7 +226,7 @@ namespace MathEngine.Parser
|
||||
|
||||
if (OperatorStack.Count > 0 && (OperatorStack.Peek().Type == TokenType.Function))
|
||||
{
|
||||
rpnQueue.Enqueue(OperatorStack.Pop());
|
||||
rpnQueue.Enqueue(OperatorStack.Pop() with { Arity = arityStack.Pop()});
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ namespace MathEngine.Tokenizer
|
||||
internal enum TokenType
|
||||
{
|
||||
None,
|
||||
GenericIdentifier,
|
||||
|
||||
Numeric,
|
||||
DecimalPoint,
|
||||
@@ -50,6 +51,7 @@ namespace MathEngine.Tokenizer
|
||||
public static readonly Token Exponentiation = new("^", TokenType.Exponentiation);
|
||||
public static readonly Token LeftParenthesis = new("(", TokenType.LeftParenthesis);
|
||||
public static readonly Token RightParenthesis = new(")", TokenType.RightParenthesis);
|
||||
public static readonly Token FunctionArgumentSeparator = new(",", TokenType.FunctionArgumentSeparator);
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the token represents any arithmetic operator.
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
|
||||
using MathEngine.Tokenizer;
|
||||
using System.ComponentModel.Design;
|
||||
using static MathEngine.Tokenizer.Token;
|
||||
|
||||
namespace MathEngine.Tokenizer
|
||||
@@ -24,6 +25,18 @@ namespace MathEngine.Tokenizer
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a character represents an ASCII letter
|
||||
/// </summary>
|
||||
/// <param name="c">The character to check</param>
|
||||
/// <returns>True if <paramref name="c"/> represents an ASCII letter, false otherwise</returns>
|
||||
|
||||
private static bool IsAsciiLetter(char c)
|
||||
{
|
||||
return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z');
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a character represents an ASCII whitespace character
|
||||
/// </summary>
|
||||
@@ -45,12 +58,22 @@ namespace MathEngine.Tokenizer
|
||||
return c == '.';
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a character represents the FunctionArgumentSeparator
|
||||
/// </summary>
|
||||
/// <param name="c">The character to check</param>
|
||||
/// <returns>True if <paramref name="c"/> represents the FunctionArgumentSeparator, false otherwise</returns>
|
||||
private static bool IsFunctionArgumentSeparator(char c)
|
||||
{
|
||||
return c == ',' ? true : false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parses the <paramref name="expression"/> to read a numeric <typeparamref name="Token"/>
|
||||
/// </summary>
|
||||
/// <param name="expression">The expression being parsed</param>
|
||||
/// <param name="at_decimal_point">Indicates if the character read before calling this method was a decimal point or not</param>
|
||||
/// <param name="current_index">The current index the parser is at</param>
|
||||
/// <param name="currentIndex">The current index the parser is at</param>
|
||||
/// <returns>A <typeparamref name="Token"/> representing the numeric value read</returns>
|
||||
private static Token ReadNumericToken(ReadOnlySpan<char> expression, bool at_decimal_point, ref Int32 currentIndex)
|
||||
{
|
||||
@@ -75,6 +98,10 @@ namespace MathEngine.Tokenizer
|
||||
ReadOnlySpan<char> errSpan = expression.Slice(currentIndex, errIndex - currentIndex);
|
||||
throw new TokenizerException($"Syntax error: The number {errSpan.ToString()} has multiple decimal point when at most one is allowed.");
|
||||
}
|
||||
else if (IsFunctionArgumentSeparator(tempChar))
|
||||
{
|
||||
break;
|
||||
}
|
||||
else if (!IsAsciiDigit(tempChar) && tempChar != '.')
|
||||
{
|
||||
break; // end of numeric token
|
||||
@@ -88,32 +115,70 @@ namespace MathEngine.Tokenizer
|
||||
return new Token(numberSpan.ToString(), TokenType.Numeric);
|
||||
}
|
||||
|
||||
|
||||
static private Token GetOperatorToken(char curChar)
|
||||
/// <summary>
|
||||
/// Parses the expression to read a GenericIdentifier Token
|
||||
/// </summary>
|
||||
/// <param name="expression">The expression being parsed</param>
|
||||
/// <param name="currentIndex">The current index the parser is at</param>
|
||||
/// <returns>A <typeparamref name="Token"/> representing a GenericIdentifier for further processing</returns>
|
||||
private static Token ReadIdentifierToken(ReadOnlySpan<char> expression, ref Int32 currentIndex)
|
||||
{
|
||||
return curChar switch
|
||||
int start = currentIndex;
|
||||
while (currentIndex + 1 < expression.Length && IsAsciiLetter(expression[currentIndex + 1]))
|
||||
{
|
||||
currentIndex++;
|
||||
}
|
||||
ReadOnlySpan<char> identifier = expression.Slice(start, currentIndex - start + 1);
|
||||
return new Token(identifier.ToString(), TokenType.GenericIdentifier, 0);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns the correct Operator Token given a ASCII Character
|
||||
/// </summary>
|
||||
/// <param name="c">The character to get the correct Operator Token for</param>
|
||||
/// <returns>A <typeparamref name="Token"/> which is represented by <paramref name="c"/> </returns>
|
||||
/// <exception cref="TokenizerException">Thrown when <paramref name="c"/> is not a recognized character for an Operator Token</exception>
|
||||
static private Token GetOperatorToken(char c)
|
||||
{
|
||||
return c switch
|
||||
{
|
||||
'+' => Token.Plus,
|
||||
'-' => Token.Minus,
|
||||
'*' => Token.Multiply,
|
||||
'/' => Token.Divide,
|
||||
'^' => Token.Exponentiation,
|
||||
_ => throw new TokenizerException($"Recieved unknown character '{curChar}' when attempting to parse an operator.")
|
||||
_ => throw new TokenizerException($"Recieved unknown character '{c}' when attempting to parse an operator.")
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
static private Token GetParenthesisToken(char curChar)
|
||||
/// <summary>
|
||||
/// Returns the correct Parenthesis Token given a ASCII Character
|
||||
/// </summary>
|
||||
/// <param name="c">The character to get the correct Parenthesis Token for</param>
|
||||
/// <returns> The correct Parenthesis <typeparamref name="Token"/> which is represented by <paramref name="c"/></returns>
|
||||
/// <exception cref="TokenizerException">Thrown when <paramref name="c"/> is not a recognized character for a Parenthesis</exception>
|
||||
static private Token GetParenthesisToken(char c)
|
||||
{
|
||||
return curChar switch
|
||||
return c switch
|
||||
{
|
||||
'(' => Token.LeftParenthesis,
|
||||
')' => Token.RightParenthesis,
|
||||
_ => throw new TokenizerException($"Recieved unknown character '{curChar}' when attempting to parse a parenthesis."),
|
||||
_ => throw new TokenizerException($"Recieved unknown character '{c}' when attempting to parse a parenthesis."),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns the Function Argument Separator Token
|
||||
/// </summary>
|
||||
/// <returns>The Function Argument Separator <typeparamref name="Token"/></returns>
|
||||
static private Token GetFunctionArgumentSeparatorToken()
|
||||
{
|
||||
return Token.FunctionArgumentSeparator;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tokenizes a given Mathematical expression given as a string to a list of tokens
|
||||
/// </summary>
|
||||
@@ -151,6 +216,12 @@ namespace MathEngine.Tokenizer
|
||||
continue;
|
||||
}
|
||||
|
||||
if (IsFunctionArgumentSeparator(curChar))
|
||||
{
|
||||
Tokenstack.Add(GetFunctionArgumentSeparatorToken());
|
||||
continue;
|
||||
}
|
||||
|
||||
//Number, two cases two consider, case 1) number is something like 142.2; case 2 .5 which is clearly 0.5.
|
||||
//Case 1
|
||||
if (IsAsciiDigit(curChar))
|
||||
@@ -162,7 +233,16 @@ namespace MathEngine.Tokenizer
|
||||
{
|
||||
Tokenstack.Add(ReadNumericToken(expression, true, ref i));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// read any iddentifiers
|
||||
// TODO: Consider how to handle imaginary unit (i). Seems like a parser issue, non-issue in symbolic
|
||||
if (IsAsciiLetter(curChar))
|
||||
{
|
||||
Tokenstack.Add(ReadIdentifierToken(expression, ref i));
|
||||
continue;
|
||||
|
||||
}
|
||||
}
|
||||
//return the stack after triming
|
||||
Tokenstack.TrimExcess();
|
||||
|
||||
373
MathEngine/MathEngine/Types/DecimalValue.cs
Normal file
373
MathEngine/MathEngine/Types/DecimalValue.cs
Normal file
@@ -0,0 +1,373 @@
|
||||
using MathEngine.Types.Interfaces;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace MathEngine.Types
|
||||
{
|
||||
internal struct DecimalValue : INumericallyOrderable<DecimalValue>, ITrigonometric<DecimalValue>
|
||||
{
|
||||
|
||||
private decimal _value;
|
||||
|
||||
private static readonly DecimalValue _zero = new(0);
|
||||
|
||||
/// <summary>
|
||||
/// The mathematical constant Pi
|
||||
/// </summary>
|
||||
private static readonly decimal PI = 3.141592653589793238462643383279502884197169399375105820974944592307816406286208998628034825342117068m;
|
||||
private static readonly decimal TWOPI = 6.283185307179586476925286766559005768394338798750211641949889184615632812572417997256069650684234136m;
|
||||
private static readonly decimal HALFPI = 1.570796326794896619231321691639751442098584699687552910487472296153908203143104499314017412671058534m;
|
||||
|
||||
// TODO: Find a set of coefficients where more values can fit into decimal while
|
||||
// keeping the accuracy just as high
|
||||
|
||||
/// <summary>
|
||||
/// Pre-computed Chebyshev coefficients for a degree 30 Sine approximation
|
||||
/// </summary>
|
||||
private static readonly decimal[] _sin_cfs = {
|
||||
1.107762718434877094787128e-31m,
|
||||
-1.823732266493779103076113e-35m,
|
||||
-9.181673072797976602053211e-29m,
|
||||
3.225262111842922385008455e-34m,
|
||||
6.446939827037235822437697e-26m,
|
||||
-2.576736665059962220360842e-33m,
|
||||
-3.868170134617741732466926e-23m,
|
||||
1.228109964113387911565451e-32m,
|
||||
1.957294106252405728115831e-20m,
|
||||
-3.882226497276181192594589e-32m,
|
||||
-8.220635246622832053855188e-18m,
|
||||
8.555581455036025700953568e-32m,
|
||||
2.811457254345518890772695e-15m,
|
||||
-1.344198394902903252583665e-31m,
|
||||
-7.647163731819816458999534e-13m,
|
||||
1.512690085816523728430496e-31m,
|
||||
1.605904383682161459928373e-10m,
|
||||
-1.207137865111693794326432e-31m,
|
||||
-2.505210838544171877505162e-8m,
|
||||
6.658710916160807720823915e-32m,
|
||||
0.000002755731922398589065255732m,
|
||||
-2.425218754430548182421541e-32m,
|
||||
-0.0001984126984126984126984127m,
|
||||
5.399220187127378949775714e-33m,
|
||||
0.008333333333333333333333333m,
|
||||
-6.429050541691068943420765e-34m,
|
||||
-0.1666666666666666666666667m,
|
||||
3.144305445341164228755108e-35m,
|
||||
1.0m,
|
||||
-2.646821446601517372109605e-37m};
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Pre-computed Chebyshev coefficients for a degree 30 Cosine approximation
|
||||
/// </summary>
|
||||
private static readonly decimal[] _cos_cfs = {
|
||||
-1.819373578408273903259426e-35m,
|
||||
3.210857395660629127502647e-30m,
|
||||
3.558623874711111508186483e-34m,
|
||||
-2.479023574256452751736573e-27m,
|
||||
-3.120601939944795774454124e-33m,
|
||||
1.611734742261012615961468e-24m,
|
||||
1.61966781283449908063686e-32m,
|
||||
-8.896791299969882965587422e-22m,
|
||||
-5.531921460818070226357292e-32m,
|
||||
4.11031762310156955385159e-19m,
|
||||
1.307520378404943821183129e-31m,
|
||||
-1.561920696858280193101514e-16m,
|
||||
-2.189425802841482649852114e-31m,
|
||||
4.779477332387381286306434e-14m,
|
||||
2.61434386212678154378457e-31m,
|
||||
-1.147074559772972468014089e-11m,
|
||||
-2.210663449693968868683802e-31m,
|
||||
2.087675698786809897901e-9m,
|
||||
1.296861671422669049472492e-31m,
|
||||
-0.0000002755731922398589065255651m,
|
||||
-5.084534256113915594062863e-32m,
|
||||
0.00002480158730158730158730159m,
|
||||
1.251597093832519719838309e-32m,
|
||||
-0.001388888888888888888888889m,
|
||||
-1.738090061385202180342415e-33m,
|
||||
0.04166666666666666666666667m,
|
||||
1.113240260965897824377927e-34m,
|
||||
-0.5m,
|
||||
-2.097353881291299259797553e-36m,
|
||||
1.0m
|
||||
};
|
||||
|
||||
|
||||
public Type UnderlyingType => throw new NotImplementedException();
|
||||
|
||||
public DecimalValue Value => throw new NotImplementedException();
|
||||
|
||||
public DecimalValue(decimal value)
|
||||
{
|
||||
_value = value;
|
||||
}
|
||||
|
||||
public DecimalValue(string value)
|
||||
{
|
||||
_value = decimal.Parse(value);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Performs radian angle range reduction into the range -Pi/2 <= x <= Pi/2
|
||||
/// </summary>
|
||||
/// <param name="x">The radian angle to reduce</param>
|
||||
/// <returns>The radian angle <paramref name="x"/> within the range -Pi/2 <= x <= Pi/2</returns>
|
||||
private static decimal RangeReduction(decimal x)
|
||||
{
|
||||
decimal r = x % TWOPI;
|
||||
|
||||
if (r > PI) r -= TWOPI;
|
||||
else if (r < -PI) r += TWOPI;
|
||||
|
||||
if (r > HALFPI) r = PI - r;
|
||||
else if (r < -HALFPI) r = -PI - r;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Evaluates a Polynommial with <typeparamref name="decimal"/> coefficients at a <typeparamref name="decimal"/> point using Horners method
|
||||
/// </summary>
|
||||
/// <param name="cfs">The coefficients of the polynomial with the leading coefficient first</param>
|
||||
/// <param name="x">The point to evaluate the polynomial at</param>
|
||||
/// <returns>The evaluation of the polynomial given by <paramref name="cfs"/> at the point <paramref name="x"/></returns>
|
||||
private static decimal HornerPolyEval(ReadOnlySpan<decimal> cfs, decimal x)
|
||||
{
|
||||
decimal result = cfs[0]; // Start with the leading coefficient
|
||||
for (int i = 1; i < cfs.Length; i++)
|
||||
{
|
||||
result = result * x + cfs[i];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds two <typeparamref name="DecimalValues"/>.
|
||||
/// </summary>
|
||||
/// <param name="a">The first <typeparamref name="DecimalValue"/></param>
|
||||
/// <param name="b">The second <typeparamref name="DecimalValue"/</param>
|
||||
/// <returns>The sum of <paramref name="a"/> and <paramref name="b"/></returns>
|
||||
public static DecimalValue operator +(DecimalValue a, DecimalValue b)
|
||||
{
|
||||
return new DecimalValue(a._value + b._value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Subtracts two <typeparamref name="DecimalValues"/>.
|
||||
/// </summary>
|
||||
/// <param name="a">The first <typeparamref name="DecimalValue"/></param>
|
||||
/// <param name="b">The second <typeparamref name="DecimalValue"/</param>
|
||||
/// <returns>The difference of <paramref name="a"/> and <paramref name="b"/></returns>
|
||||
public static DecimalValue operator -(DecimalValue a, DecimalValue b)
|
||||
{
|
||||
return new DecimalValue(a._value - b._value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Multiplies two <typeparamref name="DecimalValues"/>.
|
||||
/// </summary>
|
||||
/// <param name="a">The first <typeparamref name="DecimalValue"/></param>
|
||||
/// <param name="b">The second <typeparamref name="DecimalValue"/</param>
|
||||
/// <returns>The product of <paramref name="a"/> and <paramref name="b"/></returns>
|
||||
public static DecimalValue operator *(DecimalValue a, DecimalValue b)
|
||||
{
|
||||
return new DecimalValue(a._value * b._value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Divides two <typeparamref name="DecimalValues"/>.
|
||||
/// </summary>
|
||||
/// <param name="a">The first <typeparamref name="DecimalValue"/></param>
|
||||
/// <param name="b">The second <typeparamref name="DecimalValue"/</param>
|
||||
/// <returns>The quotient of <paramref name="a"/> and <paramref name="b"/></returns>
|
||||
/// <exception cref="DivideByZeroException">Thrown when <paramref name="b"/> is equal to zero</exception>
|
||||
public static DecimalValue operator /(DecimalValue a, DecimalValue b)
|
||||
{
|
||||
if (b._value == 0)
|
||||
{
|
||||
throw new DivideByZeroException("DIVISION BY ZERO!");
|
||||
}
|
||||
return new DecimalValue(a._value / b._value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compares two <typeparamref name="DecimalValues"/> for equality
|
||||
/// </summary>
|
||||
/// <param name="a">The first <typeparamref name="DecimalValue"/></param>
|
||||
/// <param name="b">The second <typeparamref name="DecimalValue"/</param>
|
||||
/// <returns>True if <paramref name="a"/> and <paramref name="b"/> are equal, False otherwise</returns>
|
||||
public static bool operator ==(DecimalValue a, DecimalValue b)
|
||||
{
|
||||
return a._value == b._value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compares two <typeparamref name="DecimalValue"/> for inequality
|
||||
/// </summary>
|
||||
/// <param name="a">The first <typeparamref name="DecimalValue"/></param>
|
||||
/// <param name="b">The second <typeparamref name="DecimalValue"/</param>
|
||||
/// <returns>True if <paramref name="a"/> and <paramref name="b"/> are not equal, False otherwise</returns>
|
||||
public static bool operator !=(DecimalValue a, DecimalValue b)
|
||||
{
|
||||
return a._value != b._value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compares the current <typeparamref name="DecimalValue"/> instance to another <typeparamref name="DecimalValue"/>
|
||||
/// </summary>
|
||||
/// <param name="other">The <typeparamref name="DecimalValue"/> instance to compare to the current <typeparamref name="DecimalValue"/> instance</param>
|
||||
/// <returns>A signed number indicating the relative values of this instance and value.
|
||||
/// <list type="table">
|
||||
/// <listheader>
|
||||
/// <term>Return value</term>
|
||||
/// <description>Meaning</description>
|
||||
/// </listheader>
|
||||
/// <item>
|
||||
/// <term>Less than zero</term>
|
||||
/// <description>This instance is less than <paramref name="other"/>.</description>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <term>Zero</term>
|
||||
/// <description>This instance is equal to <paramref name="other"/>.</description>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <term>Greater than zero</term>
|
||||
/// <description>This instance is greater than <paramref name="other"/>.</description>
|
||||
/// </item>
|
||||
/// </list>
|
||||
/// </returns>
|
||||
public int CompareTo(DecimalValue other)
|
||||
{
|
||||
return _value.CompareTo(other._value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compares if the current <typeparamref name="DecimalValue"/> instance is less than another <typeparamref name="DecimalValues"/> instance.
|
||||
/// </summary>
|
||||
/// <param name="other"><typeparamref name="DecimalValue"/> instance to compare the current <typeparamref name="DecimalValue"/> instanct to.</param>
|
||||
/// <returns>True if the current <typeparamref name="DecimalValue"/> instance is less thatn <paramref name="other"/>, False otherwise</returns>
|
||||
public bool LessThan(DecimalValue other)
|
||||
{
|
||||
return _value < other._value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compares if the current <typeparamref name="DecimalValue"/> instance is less than or equal to another <typeparamref name="DecimalValues"/> instance.
|
||||
/// </summary>
|
||||
/// <param name="other"><typeparamref name="DecimalValue"/> instance to compare the current <typeparamref name="DecimalValue"/> instanct to.</param>
|
||||
/// <returns>True if the current <typeparamref name="DecimalValue"/> instance is less thatn <paramref name="other"/>, False otherwise</returns>
|
||||
public bool LessThanOrEqual(DecimalValue other)
|
||||
{
|
||||
return _value <= other._value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compares if the current <typeparamref name="DecimalValue"/> instance is greater than another <typeparamref name="DecimalValues"/> instance.
|
||||
/// </summary>
|
||||
/// <param name="other"><typeparamref name="DecimalValue"/> instance to compare the current <typeparamref name="DecimalValue"/> instanct to.</param>
|
||||
/// <returns>True if the current <typeparamref name="DecimalValue"/> instance is greater thatn <paramref name="other"/>, False otherwise</returns>
|
||||
public bool GreaterThan(DecimalValue other)
|
||||
{
|
||||
return _value > other._value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compares if the current <typeparamref name="DecimalValue"/> instance is greater than or equal to another <typeparamref name="DecimalValues"/> instance.
|
||||
/// </summary>
|
||||
/// <param name="other"><typeparamref name="DecimalValue"/> instance to compare the current <typeparamref name="DecimalValue"/> instanct to.</param>
|
||||
/// <returns>True if the current <typeparamref name="DecimalValue"/> instance is greater thatn <paramref name="other"/>, False otherwise</returns>
|
||||
public bool GreaterThanOrEqual(DecimalValue other)
|
||||
{
|
||||
return _value >= other._value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the current <typeparamref name="DecimalValue"/> instance to
|
||||
/// </summary>
|
||||
/// <param name="other"></param>
|
||||
/// <returns></returns>
|
||||
public DecimalValue Add(DecimalValue other)
|
||||
{
|
||||
return this + other;
|
||||
}
|
||||
|
||||
public DecimalValue Subtract(DecimalValue other)
|
||||
{
|
||||
return this - other;
|
||||
}
|
||||
|
||||
public DecimalValue Multiply(DecimalValue other)
|
||||
{
|
||||
return this * other;
|
||||
}
|
||||
|
||||
public DecimalValue Divide(DecimalValue other)
|
||||
{
|
||||
return this / other;
|
||||
}
|
||||
|
||||
public DecimalValue Negate()
|
||||
{
|
||||
return new DecimalValue(decimal.Negate(_value));
|
||||
}
|
||||
|
||||
public bool Equal(DecimalValue other)
|
||||
{
|
||||
return _value == other._value;
|
||||
}
|
||||
|
||||
public bool NotEqual(DecimalValue other)
|
||||
{
|
||||
return !Equal(other);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return _value.GetHashCode();
|
||||
}
|
||||
|
||||
public override bool Equals(object? obj)
|
||||
{
|
||||
if (!(obj is DecimalValue))
|
||||
return false;
|
||||
|
||||
DecimalValue mys = (DecimalValue)obj;
|
||||
return mys._value == _value;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return "DecimalValue: " + _value.ToString();
|
||||
}
|
||||
|
||||
public DecimalValue Sin()
|
||||
{
|
||||
decimal reduced_x = DecimalValue.RangeReduction(_value);
|
||||
return new DecimalValue(HornerPolyEval(DecimalValue._sin_cfs, reduced_x));
|
||||
}
|
||||
|
||||
public DecimalValue Cos()
|
||||
{
|
||||
decimal reduced_x = DecimalValue.RangeReduction(_value);
|
||||
return new DecimalValue(HornerPolyEval(DecimalValue._cos_cfs, reduced_x));
|
||||
}
|
||||
|
||||
public DecimalValue Tan()
|
||||
{
|
||||
DecimalValue reduced_x = new DecimalValue(DecimalValue.RangeReduction(_value));
|
||||
return reduced_x.Sin() / reduced_x.Cos();
|
||||
|
||||
}
|
||||
|
||||
public DecimalValue Max(DecimalValue other)
|
||||
{
|
||||
return this.GreaterThanOrEqual(other) ? this : other;
|
||||
}
|
||||
|
||||
public DecimalValue Min(DecimalValue other)
|
||||
{
|
||||
return this.LessThanOrEqual(other) ? this : other;
|
||||
}
|
||||
}
|
||||
}
|
||||
79
MathEngine/MathEngine/Types/Interfaces/INumeric.cs
Normal file
79
MathEngine/MathEngine/Types/Interfaces/INumeric.cs
Normal file
@@ -0,0 +1,79 @@
|
||||
using System.Numerics;
|
||||
|
||||
namespace MathEngine.Types.Interfaces
|
||||
{
|
||||
/// <summary>
|
||||
/// Base Interface that defines numerical values
|
||||
/// </summary>
|
||||
internal interface INumeric<T> where T : struct, INumeric<T>
|
||||
{
|
||||
T Value { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Adds this numeric value to another
|
||||
/// </summary>
|
||||
/// <param name="other">The numeric value to add</param>
|
||||
/// <returns>
|
||||
/// A new <see cref="INumeric"/> instance representing the sum of this and <paramref name="other"/>
|
||||
/// </returns>
|
||||
T Add(T other);
|
||||
|
||||
/// <summary>
|
||||
/// Subtracts another numeric value from this one
|
||||
/// </summary>
|
||||
/// <param name="other">The numeric value to subtract</param>
|
||||
/// <returns>
|
||||
/// A new <see cref="INumeric"/> instance representing the result of the subtraction
|
||||
/// </returns>
|
||||
T Subtract(T other);
|
||||
|
||||
/// <summary>
|
||||
/// Multiplies this numeric value by another
|
||||
/// </summary>
|
||||
/// <param name="other">The numeric value to multiply by</param>
|
||||
/// <returns>
|
||||
/// A new <see cref="INumeric"/> instance representing the product of this and <paramref name="other"/>
|
||||
/// </returns>
|
||||
T Multiply(T other);
|
||||
|
||||
/// <summary>
|
||||
/// Divides this numeric value by another
|
||||
/// </summary>
|
||||
/// <param name="other">The numeric value to divide by</param>
|
||||
/// <returns>
|
||||
/// A new <see cref="INumeric"/> instance representing the quotient of this and <paramref name="other"/>
|
||||
/// </returns>
|
||||
T Divide(T other);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the negation of this numeric value
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A new <see cref="INumeric"/> instance representing the negated value
|
||||
/// </returns>
|
||||
T Negate();
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether this numeric value is equal to the specified other value.
|
||||
/// </summary>
|
||||
/// <param name="other">The other numeric value to compare.</param>
|
||||
/// <returns>
|
||||
/// <c>true</c> if this value is equal to <paramref name="other"/>; otherwise, <c>false</c>.
|
||||
/// </returns>
|
||||
bool Equal(T other);
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether this numeric value is not equal to the specified other value.
|
||||
/// </summary>
|
||||
/// <param name="other">The other numeric value to compare.</param>
|
||||
/// <returns>
|
||||
/// <c>true</c> if this value is not equal to <paramref name="other"/>; otherwise, <c>false</c>.
|
||||
/// </returns>
|
||||
bool NotEqual(T other);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the <see cref="Type"/> of the <see cref="INumeric"/> instance
|
||||
/// </summary>
|
||||
Type UnderlyingType { get; }
|
||||
}
|
||||
}
|
||||
9
MathEngine/MathEngine/Types/Interfaces/INumericNode.cs
Normal file
9
MathEngine/MathEngine/Types/Interfaces/INumericNode.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
namespace MathEngine.Types.Interfaces
|
||||
{
|
||||
internal interface INumericNode
|
||||
{
|
||||
Type NumericType { get; }
|
||||
|
||||
void CopyTo<T>(ref T destination) where T : struct;
|
||||
}
|
||||
}
|
||||
125
MathEngine/MathEngine/Types/Interfaces/INumericallyOrderable.cs
Normal file
125
MathEngine/MathEngine/Types/Interfaces/INumericallyOrderable.cs
Normal file
@@ -0,0 +1,125 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MathEngine.Types.Interfaces
|
||||
{
|
||||
/// <summary>
|
||||
/// Extension interface that represents a numeric value that supports ordering operations.
|
||||
/// </summary>
|
||||
internal interface INumericallyOrderable<T> : INumeric<T> where T : struct, INumericallyOrderable<T>
|
||||
{
|
||||
/// <summary>
|
||||
/// Compares this numeric value with another.
|
||||
/// </summary>
|
||||
/// <param name="other">The other comparable numeric value.</param>
|
||||
/// <returns>
|
||||
/// Less than zero if this < other, 0 if equal, greater than zero if this > other.
|
||||
/// </returns>
|
||||
int CompareTo(T other);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Determines if this numeric value is less than the specified other value.
|
||||
/// </summary>
|
||||
/// <param name="other">The other numeric value to compare.</param>
|
||||
/// <returns>True if this < other, False otherwise</returns>
|
||||
bool LessThan(T other);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether this value is less than or equal to the specified other value.
|
||||
/// </summary>
|
||||
/// <param name="other">The other numeric value to compare.</param>
|
||||
/// <returns>True if this <= other, False otherwise</returns>
|
||||
bool LessThanOrEqual(T other);
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether this value is greater than the specified other value.
|
||||
/// </summary>
|
||||
/// <param name="other">The other numeric value to compare.</param>
|
||||
/// <returns>True if this > other, False otherwise</returns>
|
||||
bool GreaterThan(T other);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether this value is less than or equal to the specified other value.
|
||||
/// </summary>
|
||||
/// <param name="other">The other numeric value to compare.</param>
|
||||
/// <returns>True if this >= other, False otherwise</returns>
|
||||
bool GreaterThanOrEqual(T other);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the maximum of this node and another node.
|
||||
/// </summary>
|
||||
/// <param name="other">The other numeric node.</param>
|
||||
/// <returns>A new node representing the maximum value.</returns>
|
||||
T Max(T other);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the minimum of this node and another node.
|
||||
/// </summary>
|
||||
/// <param name="other">The other numeric node.</param>
|
||||
/// <returns>A new node representing the minimum value.</returns>
|
||||
T Min(T other);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// AST-level interface representing a numeric node that supports ordering operations.
|
||||
/// </summary>
|
||||
internal interface INumericallyOrderable : INumericNode
|
||||
{
|
||||
/// <summary>
|
||||
/// Compares this node's value with another node.
|
||||
/// </summary>
|
||||
/// <param name="other">The other numeric node to compare.</param>
|
||||
/// <returns>
|
||||
/// Less than zero if this < other, 0 if equal, greater than zero if this > other.
|
||||
/// </returns>
|
||||
int CompareTo(INumericNode other);
|
||||
|
||||
/// <summary>
|
||||
/// Determines if this node's value is less than the other node's value.
|
||||
/// </summary>
|
||||
/// <param name="other">The other numeric node to compare.</param>
|
||||
/// <returns>True if this < other, False otherwise</returns>
|
||||
bool LessThan(INumericNode other);
|
||||
|
||||
/// <summary>
|
||||
/// Determines if this node's value is less than or equal to the other node's value.
|
||||
/// </summary>
|
||||
/// <param name="other">The other numeric node to compare.</param>
|
||||
/// <returns>True if this <= other, False otherwise</returns>
|
||||
bool LessThanOrEqual(INumericNode other);
|
||||
|
||||
/// <summary>
|
||||
/// Determines if this node's value is greater than the other node's value.
|
||||
/// </summary>
|
||||
/// <param name="other">The other numeric node to compare.</param>
|
||||
/// <returns>True if this > other, False otherwise</returns>
|
||||
bool GreaterThan(INumericNode other);
|
||||
|
||||
/// <summary>
|
||||
/// Determines if this node's value is greater than or equal to the other node's value.
|
||||
/// </summary>
|
||||
/// <param name="other">The other numeric node to compare.</param>
|
||||
/// <returns>True if this >= other, False otherwise</returns>
|
||||
bool GreaterThanOrEqual(INumericNode other);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the maximum of this node and another node.
|
||||
/// </summary>
|
||||
/// <param name="other">The other numeric node.</param>
|
||||
/// <returns>A new node representing the maximum value.</returns>
|
||||
INumericallyOrderable Max(INumericNode other);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the minimum of this node and another node.
|
||||
/// </summary>
|
||||
/// <param name="other">The other numeric node.</param>
|
||||
/// <returns>A new node representing the minimum value.</returns>
|
||||
INumericallyOrderable Min(INumericNode other);
|
||||
}
|
||||
}
|
||||
49
MathEngine/MathEngine/Types/Interfaces/ITrigonometric.cs
Normal file
49
MathEngine/MathEngine/Types/Interfaces/ITrigonometric.cs
Normal file
@@ -0,0 +1,49 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MathEngine.Types.Interfaces
|
||||
{
|
||||
/// <summary>
|
||||
/// Generic Interface that defines support for Trigonometric evaluation
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
internal interface ITrigonometric<T> where T : struct, INumeric<T>
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns the sine of this value
|
||||
/// </summary>
|
||||
T Sin();
|
||||
|
||||
/// <summary>
|
||||
/// Returns the cosine of this value
|
||||
/// </summary>
|
||||
T Cos();
|
||||
|
||||
/// <summary>
|
||||
/// Returns the tangent of this value
|
||||
/// </summary>
|
||||
T Tan();
|
||||
}
|
||||
|
||||
|
||||
internal interface ITrigonometricNode: INumericNode
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns the sine of this value
|
||||
/// </summary>
|
||||
INumericNode Sin();
|
||||
|
||||
/// <summary>
|
||||
/// Returns the cosine of this value
|
||||
/// </summary>
|
||||
INumericNode Cos();
|
||||
|
||||
/// <summary>
|
||||
/// Returns the tangent of this value
|
||||
/// </summary>
|
||||
INumericNode Tan();
|
||||
}
|
||||
}
|
||||
@@ -4,11 +4,136 @@ namespace MathRunner
|
||||
{
|
||||
internal class Program
|
||||
{
|
||||
|
||||
static readonly decimal[] SIN_COEFF = {-2.495353579956845068391614e-22m,
|
||||
5.577877028144835944149859e-21m,
|
||||
-5.907037282921367151874229e-20m,
|
||||
3.942306394997070814280735e-19m,
|
||||
-1.860603293633641604845514e-18m,
|
||||
6.605428876334905572700573e-18m,
|
||||
-1.83190469182408107133773e-17m,
|
||||
4.06860589192645688022598e-17m,
|
||||
-7.358359464590914836426366e-17m,
|
||||
1.097329189752797643166105e-16m,
|
||||
-1.441182766313743229879074e-16m,
|
||||
1.40511452613827650863302e-16m,
|
||||
2.689837823485177032543228e-15m,
|
||||
8.817888613969593259744191e-17m,
|
||||
-7.647698614509161378062996e-13m,
|
||||
2.706140963906206196147425e-17m,
|
||||
1.605904270053538957745356e-10m,
|
||||
3.932205925254663059528159e-18m,
|
||||
-2.505210838655286048763183e-8m,
|
||||
2.533366734800068550746431e-19m,
|
||||
0.000002755731922398543165479237m,
|
||||
6.481293982930866844259318e-21m,
|
||||
-0.0001984126984126984133938607m,
|
||||
5.479548578024028585883317e-23m,
|
||||
0.008333333333333333333330317m,
|
||||
1.071395263377213212738481e-25m,
|
||||
-0.1666666666666666666666667m,
|
||||
1.61588801274554481697818e-29m,
|
||||
1.0m,
|
||||
-3.751019264464499084009736e-34m};
|
||||
|
||||
static readonly decimal[] s2 = {1.107762718434877094787128e-31m,
|
||||
-1.823732266493779103076113e-35m,
|
||||
-9.181673072797976602053211e-29m,
|
||||
3.225262111842922385008455e-34m,
|
||||
6.446939827037235822437697e-26m,
|
||||
-2.576736665059962220360842e-33m,
|
||||
-3.868170134617741732466926e-23m,
|
||||
1.228109964113387911565451e-32m,
|
||||
1.957294106252405728115831e-20m,
|
||||
-3.882226497276181192594589e-32m,
|
||||
-8.220635246622832053855188e-18m,
|
||||
8.555581455036025700953568e-32m,
|
||||
2.811457254345518890772695e-15m,
|
||||
-1.344198394902903252583665e-31m,
|
||||
-7.647163731819816458999534e-13m,
|
||||
1.512690085816523728430496e-31m,
|
||||
1.605904383682161459928373e-10m,
|
||||
-1.207137865111693794326432e-31m,
|
||||
-2.505210838544171877505162e-8m,
|
||||
6.658710916160807720823915e-32m,
|
||||
0.000002755731922398589065255732m,
|
||||
-2.425218754430548182421541e-32m,
|
||||
-0.0001984126984126984126984127m,
|
||||
5.399220187127378949775714e-33m,
|
||||
0.008333333333333333333333333m,
|
||||
-6.429050541691068943420765e-34m,
|
||||
-0.1666666666666666666666667m,
|
||||
3.144305445341164228755108e-35m,
|
||||
1.0m,
|
||||
-2.646821446601517372109605e-37m};
|
||||
|
||||
|
||||
static readonly decimal[] s3 = { 9.916326246264954201615594e-30m,
|
||||
1.54319928865030320494218e-29m,
|
||||
-2.674184926393101367468514e-28m,
|
||||
-2.692817072692041357738017e-28m,
|
||||
6.587612243251582675930865e-26m,
|
||||
2.093202438542293840804485e-27m,
|
||||
-3.868835054593715102015364e-23m,
|
||||
-9.553041629841611461032692e-27m,
|
||||
1.95729616572122280801307e-20m,
|
||||
2.839955456858767175704141e-26m,
|
||||
-8.220635290513026145246676e-18m,
|
||||
-5.767530270581598294967383e-26m,
|
||||
2.811457254411262970766708e-15m,
|
||||
8.161593358681450120770325e-26m,
|
||||
-7.647163731820510961992906e-13m,
|
||||
-8.059975709448137639642111e-26m,
|
||||
1.605904383682161971192382e-10m,
|
||||
5.477723421466464288236971e-26m,
|
||||
-2.50521083854417188005417e-8m,
|
||||
-2.485058233812166433395256e-26m,
|
||||
0.00000275573192239858906526391m,
|
||||
7.148546378130704537447194e-27m,
|
||||
-0.0001984126984126984126984142m,
|
||||
-1.202593758014493393872389e-27m,
|
||||
0.008333333333333333333333333m,
|
||||
1.046429449740262240020432e-28m,
|
||||
-0.1666666666666666666666667m,
|
||||
-3.934554842474513984886404e-30m,
|
||||
1.0m,
|
||||
3.934032899734993769175678e-32m,};
|
||||
public static decimal Sin(decimal x)
|
||||
{
|
||||
decimal result = SIN_COEFF[0]; // Start with the leading coefficient
|
||||
for (int i = 1; i < SIN_COEFF.Length; i++)
|
||||
{
|
||||
result = result * x + SIN_COEFF[i];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static decimal Sin2(decimal x)
|
||||
{
|
||||
decimal result = s2[0]; // Start with the leading coefficient
|
||||
for (int i = 1; i < s2.Length; i++)
|
||||
{
|
||||
result = result * x + s2[i];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static decimal Sin3(decimal x)
|
||||
{
|
||||
decimal result = s3[0]; // Start with the leading coefficient
|
||||
for (int i = 1; i < s3.Length; i++)
|
||||
{
|
||||
result = result * x + s3[i];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static void Main(string[] args)
|
||||
{
|
||||
Console.WriteLine("Evaluting expression...");
|
||||
Expression exp = new("54+2+1");
|
||||
Console.WriteLine(exp.Evaluate());
|
||||
// Expression exp = new("sin(3.14159265)+1+max(1,2,3,4,5,6)");
|
||||
Expression exp = new("sin(3.14159265)*sin(3.14159265)+cos(3.14159265)*cos(3.14159265) + tan(1.4)");
|
||||
Console.WriteLine(exp.Evaluate().ToString());
|
||||
Console.WriteLine("Hello, World!");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user