Feature/implement exponentiation (#6)

* Modified System to make each numeric node a non-generic for now.

Implemented decimal exponentation. This is a draft implementation and is not intended for final release

* Reverted change to NumericNode and fixed tests
This commit is contained in:
0xJ1M
2023-09-19 20:44:34 +01:00
committed by GitHub
parent 461a60ff77
commit f837311da8
9 changed files with 149 additions and 21 deletions

View File

@@ -78,10 +78,8 @@ namespace EngineTests.Parser_Tests.Nodes
[TestMethod]
public void TestNodeFactoryNumericNodesOnDefinedTypes()
{
Token test_token1 = new("100", Token.Type.Numeric, Token.NumericType.Integer, 0);
Token test_token2 = new("100.5", Token.Type.Numeric, Token.NumericType.Decimal, 0);
BaseNode testNode1 = NodeFactory.CreateNumericNode(test_token1);
BaseNode testNode2 = NodeFactory.CreateNumericNode(test_token2);
}

View File

@@ -8,6 +8,7 @@
</PropertyGroup>
<ItemGroup>
<Compile Remove="Parser\Nodes\NumericIntegerNode.cs" />
<Compile Remove="Parser\Nodes\TreeNode.cs" />
</ItemGroup>
<ItemGroup>

View File

@@ -105,6 +105,28 @@ namespace MathEngine.Parser.Parser
return lhs.Divide(rhs);
}
/// <summary>
/// Base method for exponentiating two nodes, returning another BaseNode object
/// </summary>
/// <param name="otherNode">The node which will be the power to raise the current instance to</param>
/// <returns></returns>
/// <exception cref="InvalidOperationException"></exception>
protected virtual BaseNode Exponentiate(BaseNode otherNode)
{
throw new InvalidOperationException("Attempted to call BaseNode _exponentiate, which is invalid!");
}
/// <summary>
/// Base operator for exponentiating two Base Nodes
/// </summary>
/// <param name="lhs">The left BaseNode</param>
/// <param name="rhs">The right BaseNode</param>
/// <returns>The exponentiating of the two BaseNodes, as defined by the calling type</returns>
public static BaseNode operator ^(BaseNode lhs, BaseNode rhs)
{
return lhs.Exponentiate(rhs);
}
/// <summary>
/// Abstract Base method that evaluates the current Node
/// </summary>

View File

@@ -27,7 +27,7 @@ namespace MathEngine.Parser.Parser
case Token.Type.Division:
return new BinaryNode(LeftBranch, RightBranch, (a, b) => a / b);
case Token.Type.Exponentiation:
throw new NotImplementedException("Exponentiation is not implemented at this time");
return new BinaryNode(LeftBranch, RightBranch, (a, b) => a ^ b);
default:
throw new NotImplementedException("Attempted to create a BinaryNode with an invalid operation!");
}
@@ -43,9 +43,9 @@ namespace MathEngine.Parser.Parser
switch (CurrentToken.NumericalType)
{
case Token.NumericType.Integer:
return new NumericNode<Int64>(Int64.Parse(CurrentToken.TokenValue));
throw new NotImplementedException("Integer Numbers are not implemented at this time");
case Token.NumericType.Decimal:
return new NumericNode<Decimal>(Decimal.Parse(CurrentToken.TokenValue));
return new NumericNode<decimal>(Decimal.Parse(CurrentToken.TokenValue));
case Token.NumericType.Complex:
throw new NotImplementedException("Complex Numbers are not implemented at this time");
default:

View File

@@ -0,0 +1,90 @@
using MathEngine.Parser.Parser;
using MathEngine.Parser.Parser.Nodes;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MathEngine.Parser.Nodes
{
internal class NumericIntegerNode: BaseNode
{
private readonly decimal Value;
/// <summary>
/// Initialises a new instance of a NumericNode with a given Token
/// </summary>
/// <param name="value">The token for the nodes value</param>
public NumericIntegerNode(decimal value)
{
this.Children = null;
this.Value = value;
}
protected override BaseNode Add(BaseNode otherNode)
{
if (otherNode is NumericIntegerNode)
{
NumericIntegerNode rhs = (NumericIntegerNode)otherNode;
return new NumericIntegerNode(Value + rhs.Value);
}
throw new InvalidOperationException("Attempted Invalid operation");
}
protected override BaseNode Subtract(BaseNode otherNode)
{
if (otherNode is NumericIntegerNode)
{
NumericIntegerNode rhs = (NumericIntegerNode)otherNode;
return new NumericIntegerNode(Value - rhs.Value);
}
throw new InvalidOperationException("Attempted Invalid operation");
}
protected override BaseNode Multiply(BaseNode otherNode)
{
if (otherNode is NumericIntegerNode)
{
NumericIntegerNode rhs = (NumericIntegerNode)otherNode;
return new NumericIntegerNode(Value * rhs.Value);
}
throw new InvalidOperationException("Attempted Invalid operation");
}
protected override BaseNode Divide(BaseNode otherNode)
{
if (otherNode is NumericIntegerNode)
{
NumericIntegerNode rhs = (NumericIntegerNode)otherNode;
return new NumericIntegerNode(Value / rhs.Value);
}
throw new InvalidOperationException("Attempted Invalid operation");
}
protected override BaseNode Exponentiate(BaseNode otherNode)
{
if (otherNode is NumericIntegerNode)
{
NumericIntegerNode rhs = (NumericIntegerNode)otherNode;
return new NumericIntegerNode((Int64)(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()
{
return Value.ToString();
}
}
}

View File

@@ -7,13 +7,13 @@ namespace MathEngine.Parser.Parser.Nodes
/// </summary>
internal class NumericNode<T> : BaseNode where T: INumber<T>
{
private readonly T Value;
private readonly decimal Value;
/// <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(T value)
public NumericNode(decimal value)
{
this.Children = null;
this.Value = value;
@@ -21,40 +21,50 @@ namespace MathEngine.Parser.Parser.Nodes
protected override BaseNode Add(BaseNode otherNode)
{
if (otherNode is NumericNode<T>)
if (otherNode is NumericNode<decimal>)
{
NumericNode<T> rhs = (NumericNode<T>)otherNode;
return new NumericNode<T>(Value + rhs.Value);
NumericNode<decimal> rhs = (NumericNode<decimal>)otherNode;
return new NumericNode<decimal>(Value + rhs.Value);
}
throw new InvalidOperationException("Attempted Invalid operation");
}
protected override BaseNode Subtract(BaseNode otherNode)
{
if (otherNode is NumericNode<T>)
if (otherNode is NumericNode<decimal>)
{
NumericNode<T> rhs = (NumericNode<T>)otherNode;
return new NumericNode<T>(Value - rhs.Value);
NumericNode<decimal> rhs = (NumericNode<decimal>)otherNode;
return new NumericNode<decimal>(Value - rhs.Value);
}
throw new InvalidOperationException("Attempted Invalid operation");
}
protected override BaseNode Multiply(BaseNode otherNode)
{
if (otherNode is NumericNode<T>)
if (otherNode is NumericNode<decimal>)
{
NumericNode<T> rhs = (NumericNode<T>)otherNode;
return new NumericNode<T>(Value * rhs.Value);
NumericNode<decimal> rhs = (NumericNode<decimal>)otherNode;
return new NumericNode<decimal>(Value * rhs.Value);
}
throw new InvalidOperationException("Attempted Invalid operation");
}
protected override BaseNode Divide(BaseNode otherNode)
{
if (otherNode is NumericNode<T>)
if (otherNode is NumericNode<decimal>)
{
NumericNode<T> rhs = (NumericNode<T>)otherNode;
return new NumericNode<T>(Value / rhs.Value);
NumericNode<decimal> rhs = (NumericNode<decimal>)otherNode;
return new NumericNode<decimal>(Value / 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");
}

View File

@@ -119,6 +119,7 @@ namespace MathEngine.Parser.Parser
case Token.Type.Subtraction:
case Token.Type.Multiplication:
case Token.Type.Division:
case Token.Type.Exponentiation:
while ((OperatorStack.Count != 0 && ((((OperatorStack.Peek().Token_Type == Token.Type.Function) | (OperatorPrecedence(OperatorStack.Peek()) > OperatorPrecedence(CurrentToken)) | ((OperatorPrecedence(OperatorStack.Peek()) == OperatorPrecedence(CurrentToken)) & (IsLeftAssociatve(CurrentToken)))) && !(OperatorStack.Peek().Token_Type == Token.Type.LeftParenthesis)))))
{
OutputStack.Push(OperatorStack.Pop());

View File

@@ -25,6 +25,11 @@
/// </summary>
public static readonly Token Divide = new("/", Type.Division, NumericType.NaN, 0);
/// <summary>
/// Represents the token for ^
/// </summary>
public static readonly Token Exponentiation = new("^", Type.Exponentiation, NumericType.NaN, 0);
/// <summary>
/// Enum representing the token type
/// </summary>

View File

@@ -31,6 +31,7 @@
'-' => Token.Minus,
'*' => Token.Multiply,
'/' => Token.Divide,
'^' => Token.Exponentiation,
_ => throw new Exception(String.Format("Character {0} is not a defined operator", curChar)),
};
}