private void CreateEquation() { int?operatorIndex = 0; var operationsInOrder = new MathematicOperation[] { MathematicOperation.Addition, MathematicOperation.Substraction, MathematicOperation.Multiplication, MathematicOperation.Division, MathematicOperation.Power }; foreach (MathematicOperation mathOperation in operationsInOrder) { operatorIndex = IndexOfOperator(equation, mathOperation); if (operatorIndex != null) { int substringLeght = operatorIndex.Value; leftEquation = new Equation(equation.Substring(0, substringLeght)); substringLeght += 1; rightEquation = new Equation(equation.Substring(substringLeght)); operation = mathOperation; return; } } leftEquation = null; rightEquation = null; }
public void TestRebalanceMathematic(MathematicOperation op1, MathematicOperation op2, bool rebalanceExpected) { // parser will result in a right-weighted tree: A * B + C => A * {B + C} // if the left operator is a multiply or divide and the right is an add or subtract, // the left operator takes precedence and tree should be rebalanced. // // A * B + C => {A * B} + C rebalanced // A + B * C => A + {B * C} not rebalanced // var variable1 = new VariableExpression("variable1"); var variable2 = new VariableExpression("variable2"); var value = new IntegerConstantExpression(99); var clause = new MathematicExpression(variable2, op2, value); var expr = new MathematicExpression(variable1, op1, clause); var result = expr.Rebalance() as MathematicExpression; Assert.That(result, Is.Not.Null); if (rebalanceExpected) { var expectedLeft = new MathematicExpression(variable1, op1, variable2); Assert.That(result.Left, Is.EqualTo(expectedLeft)); Assert.That(result.Operation, Is.EqualTo(op2)); Assert.That(result.Right, Is.EqualTo(value)); } else { Assert.That(result.Left, Is.EqualTo(expr.Left)); Assert.That(result.Operation, Is.EqualTo(expr.Operation)); Assert.That(result.Right, Is.EqualTo(expr.Right)); } }
/// <summary> /// Applies the specified <paramref name="operation"/> and <paramref name="amount"/> to the provided <paramref name="value"/>. /// </summary> /// <param name="value">The value to modified.</param> /// <param name="operation">The operation to apply.</param> /// <param name="amount">The amount to apply.</param> /// <returns>The modified value.</returns> public static int Apply(int value, MathematicOperation operation, int amount) { switch (operation) { case MathematicOperation.Add: return(value + amount); case MathematicOperation.Subtract: return(value - amount); case MathematicOperation.Multiply: return(value * amount); case MathematicOperation.Divide: return(value / amount); case MathematicOperation.Modulus: return(value % amount); case MathematicOperation.BitwiseAnd: return(value & amount); default: return(0); } }
/// <summary> /// Validates that <paramref name="mathematicOperation" /> is valid. Base class checks the operation matches. Can be overridden to /// provide more checks. /// </summary> /// <typeparam name="T">The type we are operating on.</typeparam> /// <param name="mathematicOperation">The operation to validate.</param> protected virtual void ValidateOperation <T>(MathematicOperation <T> mathematicOperation) { if (mathematicOperation.OperationType != SupportedOperationType) { throw new ArgumentException($"Operation type {mathematicOperation.OperationType} does not match supported type {SupportedOperationType}."); } }
protected override void ValidateOperation <T>(MathematicOperation <T> mathematicOperation) { if (typeof(T) != typeof(int)) { throw new ArgumentException("Processor expects int operands."); } base.ValidateOperation(mathematicOperation); }
public void TestAppendString(MathematicOperation op, string expected) { var variable = new VariableExpression("variable"); var value = new IntegerConstantExpression(99); var expr = new MathematicExpression(variable, op, value); var builder = new StringBuilder(); expr.AppendString(builder); Assert.That(builder.ToString(), Is.EqualTo(expected)); }
public T EvaluateOperation <T>(MathematicOperation <T> operation) { if (operation == null) { throw new ArgumentNullException(nameof(operation)); } var processor = operationProcessorSelector.GetOperationProcessor(operation); return(processor.ProcessOperation(operation)); }
public IOperationProcessor GetOperationProcessor <T>(MathematicOperation <T> mathematicOperation) { var operandType = typeof(T); if (operandType == typeof(int)) { return(GetProcessorForIntOperations(mathematicOperation.OperationType)); } throw new NotImplementedException(); }
protected override T ProcessInternal <T>(MathematicOperation <T> mathematicOperation) { var sum = 0; foreach (var op in mathematicOperation.Operands) { sum += (int)(object)op; } return((T)Convert.ChangeType(sum, typeof(T))); }
protected override T ProcessInternal <T>(MathematicOperation <T> mathematicOperation) { var multResult = 1; foreach (var op in mathematicOperation.Operands) { multResult *= (int)(object)op; } return((T)Convert.ChangeType(multResult, typeof(T))); }
internal static MathematicOperation GetOppositeOperation(MathematicOperation op) { switch (op) { case MathematicOperation.Add: return(MathematicOperation.Subtract); case MathematicOperation.Subtract: return(MathematicOperation.Add); case MathematicOperation.Multiply: return(MathematicOperation.Divide); case MathematicOperation.Divide: return(MathematicOperation.Multiply); default: return(MathematicOperation.None); } }
internal static char GetOperatorCharacter(MathematicOperation operation) { switch (operation) { case MathematicOperation.Add: return('+'); case MathematicOperation.Subtract: return('-'); case MathematicOperation.Multiply: return('*'); case MathematicOperation.Divide: return('/'); case MathematicOperation.Modulus: return('%'); default: return('?'); } }
internal static MathematicPriority GetPriority(MathematicOperation operation) { switch (operation) { case MathematicOperation.Add: case MathematicOperation.Subtract: return(MathematicPriority.Add); case MathematicOperation.Multiply: case MathematicOperation.Divide: case MathematicOperation.Modulus: return(MathematicPriority.Multiply); default: return(MathematicPriority.None); } }
internal static string GetOperatorType(MathematicOperation operation) { switch (operation) { case MathematicOperation.Add: return("addition"); case MathematicOperation.Subtract: return("subtraction"); case MathematicOperation.Multiply: return("multiplication"); case MathematicOperation.Divide: return("division"); case MathematicOperation.Modulus: return("modulus"); default: return("mathematic"); } }
private int?IndexOfOperator(string equation, MathematicOperation operation) { char operationChar = (char)operation; int charIndex = 0; while (charIndex < equation.Length) { if (equation[charIndex] == '(') { charIndex++; charIndex = IndexOfClosingBracket(equation, charIndex); charIndex++; } else if (equation[charIndex] == operationChar) { return(charIndex); } else { charIndex++; } } return(null); }
private static bool MergeNonConstantMathematic(MathematicExpression mathematicLeft, MathematicOperation operation, ExpressionBase right, out ExpressionBase result) { var left = mathematicLeft.Right; result = null; var newLeft = mathematicLeft.Left; var newOperation = mathematicLeft.Operation; ExpressionBase newRight; switch (mathematicLeft.Operation) { case MathematicOperation.Add: if (operation == MathematicOperation.Add) { // (a + 3) + 2 => a + (3 + 2) if (!MergeAddition(left, right, out newRight)) { result = newRight; return(false); } } else if (operation == MathematicOperation.Subtract) { if (IsGreater(left, right)) { // (a + 3) - 2 => a + (3 - 2) if (!MergeSubtraction(left, right, out newRight)) { result = newRight; return(false); } } else { // (a + 2) - 3 => a - (3 - 2) if (!MergeSubtraction(right, left, out newRight)) { result = newRight; return(false); } newOperation = MathematicOperation.Subtract; } } else { return(false); } break; case MathematicOperation.Subtract: if (operation == MathematicOperation.Add) { if (IsGreater(left, right)) { // (a - 3) + 2 => a - (3 - 2) if (!MergeSubtraction(left, right, out newRight)) { result = newRight; return(false); } } else { // (a - 2) + 3 => a + (3 - 2) if (!MergeSubtraction(right, left, out newRight)) { result = newRight; return(false); } newOperation = MathematicOperation.Add; } } else if (operation == MathematicOperation.Subtract) { // (a - 3) - 2 => a - (3 + 2) if (!MergeAddition(left, right, out newRight)) { result = newRight; return(false); } } else { return(false); } break; case MathematicOperation.Multiply: switch (operation) { case MathematicOperation.Multiply: // (a * 3) * 2 => a * (3 * 2) if (!MergeMultiplication(left, right, out newRight)) { result = newRight; return(false); } break; case MathematicOperation.Divide: if (left.Type == ExpressionType.FloatConstant) { right = FloatConstantExpression.ConvertFrom(right); if (right.Type == ExpressionType.ParseError) { return(false); } } else if (right.Type == ExpressionType.FloatConstant) { left = FloatConstantExpression.ConvertFrom(left); if (left.Type == ExpressionType.ParseError) { return(false); } } else { // (a * 8) / 4 => a * (8 / 4) => a * 2 // can only merge these if the constant on the left is a multiple of the constant on the right if (!IsMultiple(left, right)) { return(false); } } if (!MergeDivision(left, right, out newRight)) { result = newRight; return(false); } break; case MathematicOperation.Modulus: // (a * 8) % 4 => a % 4 // can only merge these if the constant on the left is a multiple of the constant on the right if (!IsMultiple(left, right)) { return(false); } newRight = right; newOperation = MathematicOperation.Modulus; break; default: return(false); } break; case MathematicOperation.Divide: if (operation == MathematicOperation.Divide) { // (a / 3) / 2 => a / (3 * 2) var multiplication = new MathematicExpression(left, MathematicOperation.Multiply, right); if (!MergeMultiplication(left, right, out newRight)) { result = newRight; return(false); } } else if (operation == MathematicOperation.Multiply) { if (left.Type == ExpressionType.FloatConstant || right.Type == ExpressionType.FloatConstant) { // (a / 3.0) * 2.0 => a * (2.0 / 3.0) if (!MergeDivision(right, left, out newRight)) { result = newRight; return(false); } newOperation = MathematicOperation.Multiply; } else { // (a / 3) * 2 => a * (2 / 3) // when integer division is performed first, the result may be floored before applying // the multiplication, so don't automatically merge them. return(false); } } else { return(false); } break; case MathematicOperation.BitwiseAnd: // (a & 12) & 5 => a & (12 & 5) if (operation != MathematicOperation.BitwiseAnd) { return(false); } if (!MergeBitwiseAnd(left, right, out newRight)) { result = newRight; return(false); } break; default: return(false); } return(MergeOperands(newLeft, newOperation, newRight, out result)); }
private static ExpressionBase ParseMathematic(PositionalTokenizer tokenizer, ExpressionBase left, MathematicOperation operation, int joinerLine, int joinerColumn) { OperationPriority priority; switch (operation) { case MathematicOperation.Add: if (left is StringConstantExpression) { priority = OperationPriority.AppendString; break; } priority = OperationPriority.AddSubtract; break; case MathematicOperation.Subtract: priority = OperationPriority.AddSubtract; break; case MathematicOperation.Multiply: case MathematicOperation.Divide: case MathematicOperation.Modulus: priority = OperationPriority.MulDivMod; break; case MathematicOperation.BitwiseAnd: priority = OperationPriority.BitwiseAnd; break; default: return(new ParseErrorExpression("Unknown operator: " + operation)); } var right = ParseExpression(tokenizer, priority); switch (right.Type) { case ExpressionType.ParseError: return(right); case ExpressionType.Comparison: // will be rebalanced case ExpressionType.Conditional: // will be rebalanced case ExpressionType.FloatConstant: case ExpressionType.FunctionCall: case ExpressionType.IntegerConstant: case ExpressionType.Mathematic: case ExpressionType.StringConstant: case ExpressionType.Variable: break; default: var expressionTokenizer = tokenizer as ExpressionTokenizer; if (expressionTokenizer != null) { expressionTokenizer.QueueExpression(right); } right = new KeywordExpression(MathematicExpression.GetOperatorCharacter(operation).ToString(), joinerLine, joinerColumn); return(ParseError(tokenizer, "Incompatible mathematical operation", right)); } if (priority == OperationPriority.AddSubtract) { var mathematicRight = right as MathematicExpression; } return(new MathematicExpression(left, operation, right)); }
private static bool MergeFields(Field field, Term term, MathematicOperation operation) { ExpressionBase right; if (field.Type == FieldType.Value) { right = new IntegerConstantExpression((int)field.Value); } else if (field.Type == FieldType.Float) { right = new FloatConstantExpression(field.Float); } else { return(false); } ExpressionBase left = null; if (term.multiplier == 1.0) { if (term.field.Type == FieldType.Value) { left = new IntegerConstantExpression((int)term.field.Value); } else if (term.field.Type == FieldType.Float) { left = new FloatConstantExpression(term.field.Float); } } if (left == null) { FloatConstantExpression floatRight; switch (operation) { case MathematicOperation.Multiply: floatRight = FloatConstantExpression.ConvertFrom(right) as FloatConstantExpression; if (floatRight == null) { return(false); } term.multiplier *= floatRight.Value; return(true); case MathematicOperation.Divide: floatRight = FloatConstantExpression.ConvertFrom(right) as FloatConstantExpression; if (floatRight == null) { return(false); } term.multiplier /= floatRight.Value; return(true); default: return(false); } } var mathematicExpression = new MathematicExpression(left, operation, right); term.field = AchievementBuilder.CreateFieldFromExpression(mathematicExpression.MergeOperands()); return(true); }
public T ProcessOperation <T>(MathematicOperation <T> mathematicOperation) { ValidateOperation(mathematicOperation); return(ProcessInternal(mathematicOperation)); }
protected abstract T ProcessInternal <T>(MathematicOperation <T> mathematicOperation);
private static bool MergeOperands(ExpressionBase left, MathematicOperation operation, ExpressionBase right, out ExpressionBase result) { // ASSERT: expression tree has already been rebalanced and variables have been replaced if (!left.IsLiteralConstant) { // cannot combine non-constant, leave as is. if (right.IsLiteralConstant) { // attempt to merge the right constant into the left mathematic expression var mathematicLeft = left as MathematicExpression; if (mathematicLeft != null && MergeNonConstantMathematic(mathematicLeft, operation, right, out result)) { return(true); } // attempt to eliminate the right constant if (MergeIdentity(left, operation, right, out result)) { return(true); } if (result is ParseErrorExpression) { return(false); } } } else if (!right.IsLiteralConstant) { // cannot combine non-constant. when possible, prefer constants on the right. switch (operation) { case MathematicOperation.Add: case MathematicOperation.Multiply: if (MergeIdentity(right, operation, left, out result)) { return(true); } if (result is ParseErrorExpression) { return(false); } var temp = left; left = right; right = temp; break; default: // cannot swap break; } } else { switch (operation) { case MathematicOperation.Add: return(MergeAddition(left, right, out result)); case MathematicOperation.Subtract: return(MergeSubtraction(left, right, out result)); case MathematicOperation.Multiply: return(MergeMultiplication(left, right, out result)); case MathematicOperation.Divide: return(MergeDivision(left, right, out result)); case MathematicOperation.Modulus: return(MergeModulus(left, right, out result)); case MathematicOperation.BitwiseAnd: return(MergeBitwiseAnd(left, right, out result)); } } result = new MathematicExpression(left, operation, right); return(true); }
public MathematicExpression(ExpressionBase left, MathematicOperation operation, ExpressionBase right) : base(left, right, ExpressionType.Mathematic) { Operation = operation; }
/// <summary> /// Initializes a new instance of the <see cref="ValueModifier"/> struct. /// </summary> /// <param name="operation">The operation to apply.</param> /// <param name="amount">The amount to apply.</param> public ValueModifier(MathematicOperation operation, int amount) { Operation = operation; Amount = amount; }
private static bool MergeIdentity(ExpressionBase left, MathematicOperation operation, ExpressionBase right, out ExpressionBase result) { bool isZero = false; bool isOne = false; var integerRight = right as IntegerConstantExpression; if (integerRight != null) { isZero = (integerRight.Value == 0); isOne = (integerRight.Value == 1); } else { var floatRight = right as FloatConstantExpression; if (floatRight != null) { isZero = floatRight.Value == 0.0f; isOne = floatRight.Value == 1.0f; } } if (isZero) { switch (operation) { case MathematicOperation.Add: case MathematicOperation.Subtract: // anything plus or minus 0 is itself result = left; return(true); case MathematicOperation.Multiply: case MathematicOperation.BitwiseAnd: // anything times 0 is 0 // anything bitwise and 0 is 0 result = right; return(true); case MathematicOperation.Divide: case MathematicOperation.Modulus: result = new ParseErrorExpression("Division by zero"); return(false); } } else if (isOne) { switch (operation) { case MathematicOperation.Multiply: case MathematicOperation.Divide: // anything multiplied or divided by 1 is itself result = left; return(true); case MathematicOperation.Modulus: // anything modulus 1 is 0 result = new IntegerConstantExpression(0); return(true); } } result = null; return(false); }
private static ExpressionBase ParseMathematic(PositionalTokenizer tokenizer, ExpressionBase left, MathematicOperation operation, int joinerLine, int joinerColumn) { var right = ExpressionBase.Parse(tokenizer); switch (right.Type) { case ExpressionType.ParseError: return(right); case ExpressionType.Comparison: case ExpressionType.Conditional: case ExpressionType.Dictionary: case ExpressionType.FunctionCall: case ExpressionType.IntegerConstant: case ExpressionType.Mathematic: case ExpressionType.StringConstant: case ExpressionType.Variable: break; default: ParseError(tokenizer, "incompatible mathematical operation", new KeywordExpression(MathematicExpression.GetOperatorCharacter(operation).ToString(), joinerLine, joinerColumn)); break; } return(new MathematicExpression(left, operation, right)); }