WIP: Implemented a rough system for function evaluation

This commit is contained in:
Jim
2025-10-29 22:32:22 +00:00
parent c527e59b57
commit 16c27a0070
17 changed files with 1147 additions and 86 deletions

View File

@@ -4,6 +4,7 @@ using MathEngine.AST.Nodes;
using MathEngine.Tokenizer; using MathEngine.Tokenizer;
using static MathEngine.Tokenizer.Token; using static MathEngine.Tokenizer.Token;
using MathEngine.Types;
namespace EngineTests.Parser_Tests.Nodes namespace EngineTests.Parser_Tests.Nodes
{ {
@@ -18,8 +19,8 @@ namespace EngineTests.Parser_Tests.Nodes
[Fact] [Fact]
public void TestNodeFactoryBinaryNodesOnDefinedOperations() public void TestNodeFactoryBinaryNodesOnDefinedOperations()
{ {
NumericNode<decimal> node1 = new(200); NumericNode node1 = new(new DecimalValue(200));
NumericNode<decimal> node2 = new(100); NumericNode node2 = new(new DecimalValue(100));
Token plus = Token.Plus; Token plus = Token.Plus;
Token minus = Token.Minus; Token minus = Token.Minus;

View File

@@ -1,11 +1,4 @@
using System; namespace MathEngine.AST.Nodes
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using System.Text;
using System.Threading.Tasks;
namespace MathEngine.AST.Nodes
{ {
/// <summary> /// <summary>
/// Abstract class representing a Node in a Tree structure /// Abstract class representing a Node in a Tree structure

View File

@@ -1,21 +1,23 @@
using System; using MathEngine.Types.Interfaces;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Numerics; using System.Numerics;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using static MathEngine.Functions.BuiltIns.BuiltIns;
namespace MathEngine.AST.Nodes namespace MathEngine.AST.Nodes
{ {
/// <summary> /// <summary>
/// Represents a Tree node that stores a function /// Represents a Tree node that stores a function
/// </summary> /// </summary>
internal class FunctionNode<T> : BaseNode where T : INumber<T> internal class FunctionNode : BaseNode
{ {
Action func; private FunctionWrapper func;
List<NumericNode<T>> args; private INumericNode[] args;
public FunctionNode(Action Function, List<NumericNode<T>> arguments) public FunctionNode(FunctionWrapper Function, INumericNode[] arguments)
{ {
func = Function; func = Function;
args = arguments; args = arguments;
@@ -23,7 +25,7 @@ namespace MathEngine.AST.Nodes
public override BaseNode Evaluate() public override BaseNode Evaluate()
{ {
throw new NotImplementedException(); return func(args);
} }
} }
} }

View File

@@ -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; using static MathEngine.Tokenizer.Token;
namespace MathEngine.AST.Nodes namespace MathEngine.AST.Nodes
@@ -32,7 +36,7 @@ namespace MathEngine.AST.Nodes
return new BinaryNode(LeftBranch, RightBranch, (a, b) => a / b); return new BinaryNode(LeftBranch, RightBranch, (a, b) => a / b);
case TokenType.Exponentiation: case TokenType.Exponentiation:
throw new NotImplementedException("Exponentiation is not supported at this time!"); 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: default:
throw new NotImplementedException("Attempted to create a BinaryNode with an invalid operation!"); throw new NotImplementedException("Attempted to create a BinaryNode with an invalid operation!");
} }
@@ -46,23 +50,25 @@ namespace MathEngine.AST.Nodes
/// <returns></returns> /// <returns></returns>
public static BaseNode CreateFunctionNode(Token FunctionToken, Stack<BaseNode> ArgumentStack) public static BaseNode CreateFunctionNode(Token FunctionToken, Stack<BaseNode> ArgumentStack)
{ {
throw new NotImplementedException("Not implemented at this time"); int argc = (int)FunctionToken.Arity;
/* 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)
{
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> /// <summary>
@@ -81,7 +87,7 @@ namespace MathEngine.AST.Nodes
{ {
throw new ArgumentException("Failure to parse number"); throw new ArgumentException("Failure to parse number");
} }
return new NumericNode<decimal>(res); return new NumericallyOrderableNode<DecimalValue>(new DecimalValue(res));
} }
} }

View File

@@ -1,87 +1,115 @@
using System.Numerics; using MathEngine.Types.Interfaces;
using System.Numerics;
using System.Runtime.CompilerServices;
namespace MathEngine.AST.Nodes namespace MathEngine.AST.Nodes
{ {
/// <summary> /// <summary>
/// Represents a Tree node that can store a numeric value /// Represents a Tree node that can store a numeric value
/// </summary> /// </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> /// <summary>
/// Initialises a new instance of a NumericNode with a given Token /// Initialises a new instance of a NumericNode with a given Token
/// </summary> /// </summary>
/// <param name="value">The token for the nodes value</param> /// <param name="value">The token for the nodes value</param>
public NumericNode(decimal value) public NumericNode(T value)
{ {
Children = null; Children = null;
Value = value; _value = value;
} }
protected override BaseNode Add(BaseNode otherNode) 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<T>(_value.Add(rhs._value));
return new NumericNode<decimal>(Value + rhs.Value);
} }
throw new InvalidOperationException("Attempted Invalid operation"); throw new InvalidOperationException("Attempted Invalid operation");
} }
protected override BaseNode Subtract(BaseNode otherNode) protected override BaseNode Subtract(BaseNode otherNode)
{ {
if (otherNode is NumericNode<decimal>) if (otherNode is NumericNode<T>)
{ {
NumericNode<decimal> rhs = (NumericNode<decimal>)otherNode; NumericNode<T> rhs = (NumericNode<T>)otherNode;
return new NumericNode<decimal>(Value - rhs.Value); return new NumericNode<T>(_value.Subtract(rhs._value));
} }
throw new InvalidOperationException("Attempted Invalid operation"); throw new InvalidOperationException("Attempted Invalid operation");
} }
protected override BaseNode Multiply(BaseNode otherNode) protected override BaseNode Multiply(BaseNode otherNode)
{ {
if (otherNode is NumericNode<decimal>) if (otherNode is NumericNode<T>)
{ {
NumericNode<decimal> rhs = (NumericNode<decimal>)otherNode; NumericNode<T> rhs = (NumericNode<T>)otherNode;
return new NumericNode<decimal>(Value * rhs.Value); return new NumericNode<T>(_value.Multiply(rhs._value));
} }
throw new InvalidOperationException("Attempted Invalid operation"); throw new InvalidOperationException("Attempted Invalid operation");
} }
protected override BaseNode Divide(BaseNode otherNode) protected override BaseNode Divide(BaseNode otherNode)
{ {
if (otherNode is NumericNode<decimal>) if (otherNode is NumericNode<T>)
{ {
NumericNode<decimal> rhs = (NumericNode<decimal>)otherNode; NumericNode<T> rhs = (NumericNode<T>)otherNode;
return new NumericNode<decimal>(Value / rhs.Value); return new NumericNode<T>(_value.Divide(rhs._value));
} }
throw new InvalidOperationException("Attempted Invalid operation"); 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() public override BaseNode Evaluate()
{ {
return this; 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();
}
} }
} }

View 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();
}
}
}

View File

@@ -1,5 +1,6 @@
using MathEngine.AST; using MathEngine.AST;
using MathEngine.AST.Nodes; using MathEngine.AST.Nodes;
using MathEngine.Types;
namespace MathEngine.Expression namespace MathEngine.Expression
{ {

View 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);
////}
}
}

View File

@@ -1,4 +1,5 @@
using MathEngine.Tokenizer; using MathEngine.Functions.BuiltIns;
using MathEngine.Tokenizer;
using System.Security; using System.Security;
using static MathEngine.Tokenizer.Token; using static MathEngine.Tokenizer.Token;
@@ -155,8 +156,10 @@ namespace MathEngine.Parser
Stack<Token> OperatorStack = new(Expression.Count/2); Stack<Token> OperatorStack = new(Expression.Count/2);
//The final stack to return //The final stack to return
Queue<Token> rpnQueue = new(Expression.Count); 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 PreviousToken = Token.None;
Token CurrentToken; Token CurrentToken;
@@ -179,8 +182,33 @@ namespace MathEngine.Parser
PopOperatorsToQueue(OperatorStack, rpnQueue, CurrentToken); PopOperatorsToQueue(OperatorStack, rpnQueue, CurrentToken);
OperatorStack.Push(CurrentToken); OperatorStack.Push(CurrentToken);
break; break;
case TokenType.Function: case TokenType.GenericIdentifier:
OperatorStack.Push(CurrentToken); // 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; break;
case TokenType.LeftParenthesis: case TokenType.LeftParenthesis:
OperatorStack.Push(CurrentToken); OperatorStack.Push(CurrentToken);
@@ -198,7 +226,7 @@ namespace MathEngine.Parser
if (OperatorStack.Count > 0 && (OperatorStack.Peek().Type == TokenType.Function)) if (OperatorStack.Count > 0 && (OperatorStack.Peek().Type == TokenType.Function))
{ {
rpnQueue.Enqueue(OperatorStack.Pop()); rpnQueue.Enqueue(OperatorStack.Pop() with { Arity = arityStack.Pop()});
} }
break; break;
} }

View File

@@ -17,6 +17,7 @@ namespace MathEngine.Tokenizer
internal enum TokenType internal enum TokenType
{ {
None, None,
GenericIdentifier,
Numeric, Numeric,
DecimalPoint, DecimalPoint,
@@ -50,6 +51,7 @@ namespace MathEngine.Tokenizer
public static readonly Token Exponentiation = new("^", TokenType.Exponentiation); public static readonly Token Exponentiation = new("^", TokenType.Exponentiation);
public static readonly Token LeftParenthesis = new("(", TokenType.LeftParenthesis); public static readonly Token LeftParenthesis = new("(", TokenType.LeftParenthesis);
public static readonly Token RightParenthesis = new(")", TokenType.RightParenthesis); public static readonly Token RightParenthesis = new(")", TokenType.RightParenthesis);
public static readonly Token FunctionArgumentSeparator = new(",", TokenType.FunctionArgumentSeparator);
/// <summary> /// <summary>
/// Returns true if the token represents any arithmetic operator. /// Returns true if the token represents any arithmetic operator.

View File

@@ -1,5 +1,6 @@
using MathEngine.Tokenizer; using MathEngine.Tokenizer;
using System.ComponentModel.Design;
using static MathEngine.Tokenizer.Token; using static MathEngine.Tokenizer.Token;
namespace MathEngine.Tokenizer 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> /// <summary>
/// Checks if a character represents an ASCII whitespace character /// Checks if a character represents an ASCII whitespace character
/// </summary> /// </summary>
@@ -45,12 +58,22 @@ namespace MathEngine.Tokenizer
return c == '.'; 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> /// <summary>
/// Parses the <paramref name="expression"/> to read a numeric <typeparamref name="Token"/> /// Parses the <paramref name="expression"/> to read a numeric <typeparamref name="Token"/>
/// </summary> /// </summary>
/// <param name="expression">The expression being parsed</param> /// <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="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> /// <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) 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); 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."); 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 != '.') else if (!IsAsciiDigit(tempChar) && tempChar != '.')
{ {
break; // end of numeric token break; // end of numeric token
@@ -88,32 +115,70 @@ namespace MathEngine.Tokenizer
return new Token(numberSpan.ToString(), TokenType.Numeric); return new Token(numberSpan.ToString(), TokenType.Numeric);
} }
/// <summary>
static private Token GetOperatorToken(char curChar) /// 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.Plus,
'-' => Token.Minus, '-' => Token.Minus,
'*' => Token.Multiply, '*' => Token.Multiply,
'/' => Token.Divide, '/' => Token.Divide,
'^' => Token.Exponentiation, '^' => 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.LeftParenthesis,
')' => Token.RightParenthesis, ')' => 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> /// <summary>
/// Tokenizes a given Mathematical expression given as a string to a list of tokens /// Tokenizes a given Mathematical expression given as a string to a list of tokens
/// </summary> /// </summary>
@@ -151,6 +216,12 @@ namespace MathEngine.Tokenizer
continue; 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. //Number, two cases two consider, case 1) number is something like 142.2; case 2 .5 which is clearly 0.5.
//Case 1 //Case 1
if (IsAsciiDigit(curChar)) if (IsAsciiDigit(curChar))
@@ -162,7 +233,16 @@ namespace MathEngine.Tokenizer
{ {
Tokenstack.Add(ReadNumericToken(expression, true, ref i)); Tokenstack.Add(ReadNumericToken(expression, true, ref i));
continue; 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 //return the stack after triming
Tokenstack.TrimExcess(); Tokenstack.TrimExcess();

View 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 &lt;= x &lt;= Pi/2
/// </summary>
/// <param name="x">The radian angle to reduce</param>
/// <returns>The radian angle <paramref name="x"/> within the range -Pi/2 &lt;= x &lt;= 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;
}
}
}

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

View File

@@ -0,0 +1,9 @@
namespace MathEngine.Types.Interfaces
{
internal interface INumericNode
{
Type NumericType { get; }
void CopyTo<T>(ref T destination) where T : struct;
}
}

View 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 &lt; other, 0 if equal, greater than zero if this &gt; 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 &lt; 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 &lt;= 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 &gt; 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 &gt;= 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 &lt; other, 0 if equal, greater than zero if this &gt; 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 &lt; 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 &lt;= 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 &gt; 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 &gt;= 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);
}
}

View 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();
}
}

View File

@@ -4,11 +4,136 @@ namespace MathRunner
{ {
internal class Program 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) static void Main(string[] args)
{ {
Console.WriteLine("Evaluting expression..."); Console.WriteLine("Evaluting expression...");
Expression exp = new("54+2+1"); // Expression exp = new("sin(3.14159265)+1+max(1,2,3,4,5,6)");
Console.WriteLine(exp.Evaluate()); 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!"); Console.WriteLine("Hello, World!");
} }
} }