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;
        }
示例#2
0
        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));
            }
        }
示例#3
0
        /// <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);
            }
        }
示例#4
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}.");
     }
 }
示例#5
0
        protected override void ValidateOperation <T>(MathematicOperation <T> mathematicOperation)
        {
            if (typeof(T) != typeof(int))
            {
                throw new ArgumentException("Processor expects int operands.");
            }

            base.ValidateOperation(mathematicOperation);
        }
示例#6
0
        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));
        }
示例#7
0
        public T EvaluateOperation <T>(MathematicOperation <T> operation)
        {
            if (operation == null)
            {
                throw new ArgumentNullException(nameof(operation));
            }

            var processor = operationProcessorSelector.GetOperationProcessor(operation);

            return(processor.ProcessOperation(operation));
        }
示例#8
0
        public IOperationProcessor GetOperationProcessor <T>(MathematicOperation <T> mathematicOperation)
        {
            var operandType = typeof(T);

            if (operandType == typeof(int))
            {
                return(GetProcessorForIntOperations(mathematicOperation.OperationType));
            }

            throw new NotImplementedException();
        }
示例#9
0
        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)));
        }
示例#11
0
        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);
            }
        }
示例#12
0
        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('?');
            }
        }
示例#13
0
        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);
            }
        }
示例#14
0
        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);
        }
示例#16
0
        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));
        }
示例#17
0
        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));
        }
示例#18
0
        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);
        }
示例#19
0
        public T ProcessOperation <T>(MathematicOperation <T> mathematicOperation)
        {
            ValidateOperation(mathematicOperation);

            return(ProcessInternal(mathematicOperation));
        }
示例#20
0
 protected abstract T ProcessInternal <T>(MathematicOperation <T> mathematicOperation);
示例#21
0
        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);
        }
示例#22
0
 public MathematicExpression(ExpressionBase left, MathematicOperation operation, ExpressionBase right)
     : base(left, right, ExpressionType.Mathematic)
 {
     Operation = operation;
 }
示例#23
0
 /// <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;
 }
示例#24
0
        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);
        }
示例#25
0
        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));
        }