Beispiel #1
0
        /// <summary>
        /// Creates a Formula from a string that consists of an infix expression written as
        /// described in the class comment.  If the expression is syntactically incorrect,
        /// throws a FormulaFormatException with an explanatory Message.
        ///
        /// The associated normalizer and validator are the second and third parameters,
        /// respectively.
        ///
        /// If the formula contains a variable v such that normalize(v) is not a legal variable,
        /// throws a FormulaFormatException with an explanatory message.
        ///
        /// If the formula contains a variable v such that isValid(normalize(v)) is false,
        /// throws a FormulaFormatException with an explanatory message.
        ///
        /// Suppose that N is a method that converts all the letters in a string to upper case, and
        /// that V is a method that returns true only if a string consists of one letter followed
        /// by one digit.  Then:
        ///
        /// new Formula("x2+y3", N, V) should succeed
        /// new Formula("x+y3", N, V) should throw an exception, since V(N("x")) is false
        /// new Formula("2x+y3", N, V) should throw an exception, since "2x+y3" is syntactically incorrect.
        /// </summary>
        public Formula(String formula, Func <string, string> normalize, Func <string, bool> isValid)
        {
            values    = new Stack <double>();
            operators = new Stack <string>();
            fE        = gibberish;
            variables = new ArrayList();

            if (normalize == null)
            {
                normalizer = s => s;
                validator  = isValid;
            }
            else if (isValid == null)
            {
                normalizer = normalize;
                validator  = s => true;
            }
            else
            {
                normalizer = normalize;
                validator  = isValid;
            }

            FormulaConstructorMethod(formula, normalizer, validator);
        }
Beispiel #2
0
 /// <summary>
 /// Integer and Variable tokens use the same instructions, so to avoid redundant code, I included this function.
 ///
 /// </summary>
 /// <param name="currVal">Integer or Variable Value</param>
 private void VariableDoubleInstructions(double currVal)
 {
     //Exception case: The stack is empty, and can't be "peeked"
     if (operators.Count > 0 && operators.Peek().Equals("*"))
     {
         operators.Pop();
         currVal *= values.Pop();
         values.Push(currVal);
     }
     else if (operators.Count > 0 && operators.Peek().Equals("/"))
     {
         operators.Pop();
         double placeHolder = values.Pop();
         currVal = placeHolder / currVal;
         if (double.IsInfinity(currVal))
         {
             fE = new FormulaError("Cannot divide by 0");
         }
         else
         {
             values.Push(currVal);
         }
     }
     else
     {
         values.Push(currVal);
     }
 }
Beispiel #3
0
        /// Takes two ints and an operator (+, -, *, or /) and completes the operation
        private object Compute(double val1, double val2, string optr)
        {
            object result = new FormulaError("Error: Incorrect operator");

            // Addition
            if (optr.Equals("+"))
            {
                result = val1 + val2;
            }

            // Subtraction
            else if (optr.Equals("-"))
            {
                result = val1 - val2;
            }

            // Multiplication
            else if (optr.Equals("*"))
            {
                result = val1 * val2;
            }

            // Division
            else if (optr.Equals("/"))
            {
                if (val2 == 0)
                {
                    return(new FormulaError("Error: Divided by zero"));
                }
                result = val1 / val2;
            }

            return(result);
        }
        /// <summary>
        /// Gets numbers and operator to find result
        /// </summary>
        /// <param name="num2">Second number of equation</param>
        /// <param name="num1">First number of equation</param>
        /// <param name="oper">Operator to be applied to nums</param>
        /// <exception cref="System.ArgumentException">Thrown when trying to divide by zero</exception>
        /// <returns>Value found from the equation</returns>
        private static double Solve(double num2, double num1, char oper, ref object error)
        {
            switch (oper)
            {
            case '+':
                return(num1 + num2);

            case '-':
                return(num1 - num2);

            case '*':
                return(num1 * num2);

            case '/':
                if (num2 == 0)
                {
                    error = new FormulaError("Attempted to divide by zero.");
                    return(0);
                }
                else
                {
                    return(num1 / num2);
                }
            }
            return(0);
        }
Beispiel #5
0
        /// <summary>
        /// String of an operator does algebra on the numbers given, returns value after algebra
        /// </summary>
        /// <param name="given"></param>
        private object OperatorAlgebra(string operation, double num1, double num2)
        {
            double sum = 0;

            if (operation == "+")
            {
                sum = num1 + num2;
                op.Pop();
            }
            else if (operation == "-")
            {
                sum = num1 - num2;
                op.Pop();
            }
            else if (operation == "*")
            {
                sum = num1 * num2;
                op.Pop();
            }
            else if (operation == "/")
            {
                if (num2 == 0.0)
                {
                    FormulaError formulaError = new FormulaError("Divisable by zero error");
                    return(formulaError);
                }
                sum = num1 / num2;
                op.Pop();
            }
            return(sum);
        }
Beispiel #6
0
        /// <summary>
        /// Creates a Formula from a string that consists of an infix expression written as
        /// described in the class comment.  If the expression is syntactically invalid,
        /// throws a FormulaFormatException with an explanatory Message.
        ///
        /// The associated normalizer is the identity function, and the associated validator
        /// maps every string to true.
        /// </summary>
        public Formula(String formula) :
            this(formula, s => s, s => true)
        {
            values    = new Stack <double>();
            operators = new Stack <string>();
            fE        = gibberish;
            variables = new ArrayList();

            normalizer = s => s;
            validator  = s => true;

            FormulaConstructorMethod(formula, normalizer, validator);
        }
Beispiel #7
0
        /// <summary>
        /// Evaluates this Formula, using the lookup delegate to determine the values of
        /// variables.  When a variable symbol v needs to be determined, it should be looked up
        /// via lookup(normalize(v)). (Here, normalize is the normalizer that was passed to
        /// the constructor.)
        ///
        /// For example, if L("x") is 2, L("X") is 4, and N is a method that converts all the letters
        /// in a string to upper case:
        ///
        /// new Formula("x+7", N, s => true).Evaluate(L) is 11
        /// new Formula("x+7").Evaluate(L) is 9
        ///
        /// Given a variable symbol as its parameter, lookup returns the variable's value
        /// (if it has one) or throws an ArgumentException (otherwise).
        ///
        /// If no undefined variables or divisions by zero are encountered when evaluating
        /// this Formula, the value is returned.  Otherwise, a FormulaError is returned.
        /// The Reason property of the FormulaError should have a meaningful explanation.
        ///
        /// This method should never throw an exception.
        /// </summary>
        public object Evaluate(Func <string, double> lookup)
        {
            Stack <string> operatorStack = new Stack <string>();
            Stack <double> valueStack    = new Stack <double>();

            //Using a try catch to catch a divide by 0 error, or an argument exception error from the lookup
            //If these exceptions are caught, it will return a formula error.
            //If not, it will return a double.
            try
            {
                foreach (string s in ValidTokens)
                {
                    if (IsLeftParen(s))
                    {
                        operatorStack.Push(s);
                    }
                    else if (IsRightParen(s))
                    {
                        if (IsPlusOrMinusOnStack(operatorStack))
                        {
                            double computedValue = PerformPlusMinusComputation(operatorStack, valueStack);
                            valueStack.Push(computedValue);
                        }
                        operatorStack.Pop();
                        if (IsMultiplyOrDivideOnStack(operatorStack))
                        {
                            double computedValue = PerformMultiplyDivideComputation(operatorStack, valueStack, 0, false);
                            valueStack.Push(computedValue);
                        }
                    }
                    else if (IsMultiplyOrDivide(s))
                    {
                        operatorStack.Push(s);
                    }
                    else if (IsPlusOrMinus(s))
                    {
                        if (IsPlusOrMinusOnStack(operatorStack))
                        {
                            double computedValue = PerformPlusMinusComputation(operatorStack, valueStack);
                            valueStack.Push(computedValue);
                        }
                        operatorStack.Push(s);
                    }
                    else if (IsNum(s))
                    {
                        double value = double.Parse(s);
                        HandleIntOrVariable(operatorStack, valueStack, value);
                    }
                    else if (IsVariable(s))
                    {
                        double value = lookup(s);
                        HandleIntOrVariable(operatorStack, valueStack, value);
                    }
                }
                //End of for loop

                if (operatorStack.IsEmpty())
                {
                    return(valueStack.Pop());
                }
                else
                {
                    return(PerformPlusMinusComputation(operatorStack, valueStack));
                }
            }
            catch (ArgumentException)
            {
                FormulaError error = new FormulaError("Invalid variable");
                return(error);
            }
            catch (DivideByZeroException)
            {
                FormulaError error = new FormulaError("Division by 0");
                return(error);
            }
        }
Beispiel #8
0
 public void PublicGradingTestDivideByZero()
 {
     Formula x = new Formula("5/0");
     FormulaError error_message = new FormulaError("Can not divide by zero.");
     Assert.AreEqual(error_message, x.Evaluate(s => 0.0));
 }
Beispiel #9
0
        /// <summary>
        /// Evaluates this Formula, using the lookup delegate to determine the values of
        /// variables.  When a variable symbol v needs to be determined, it should be looked up
        /// via lookup(normalize(v)). (Here, normalize is the normalizer that was passed to
        /// the constructor.)
        ///
        /// For example, if L("x") is 2, L("X") is 4, and N is a method that converts all the letters
        /// in a string to upper case:
        ///
        /// new Formula("x+7", N, s => true).Evaluate(L) is 11
        /// new Formula("x+7").Evaluate(L) is 9
        ///
        /// Given a variable symbol as its parameter, lookup returns the variable's value
        /// (if it has one) or throws an ArgumentException (otherwise).
        ///
        /// If no undefined variables or divisions by zero are encountered when evaluating
        /// this Formula, the value is returned.  Otherwise, a FormulaError is returned.
        /// The Reason property of the FormulaError should have a meaningful explanation.
        ///
        /// This method should never throw an exception.
        /// </summary>
        public object Evaluate(Func <string, double> lookup)
        {
            //These stacks are used to hold the operators and operands of the infix expression
            Stack <double> values       = new Stack <double>();
            Stack <String> operators    = new Stack <String>();
            FormulaError   divideByZero = new FormulaError("Error: Divide by 0. Check expression for any possible " +
                                                           "divsions by 0.");

            //Traverses through the string
            foreach (String token in GetTokens(normalizedExp))
            {
                //If the token is an integer
                if (Double.TryParse(token, out double parsedDouble))
                {
                    if (!HandleDouble(parsedDouble, operators, values))
                    {
                        return(divideByZero);
                    }
                }

                //If the token is a variable (checks to see if the first character is a letter)
                else if (token.StartsAsVar())
                {
                    //Try to lookup the variable and pass it to the same function that handles a normal integer
                    //If an exception is thrown by the delegate looking up the variable, return an error
                    try
                    {
                        parsedDouble = lookup(token);
                        if (!HandleDouble(parsedDouble, operators, values))
                        {
                            return(divideByZero);
                        }
                    }
                    catch (Exception)
                    {
                        return(new FormulaError("A value for the variable " + token + "could not be found. " +
                                                "Check the variable or the lookup delegate"));
                    }
                }

                //If the token is + or -
                else if (token.Equals("+") || token.Equals("-"))
                {
                    HandlePlusMinus(operators, values);

                    //Pushes the token (+ or -) onto the stack always
                    operators.Push(token);
                }

                //If the token is * or / or (
                else if (token.Equals("*") || token.Equals("/") || token.Equals("("))
                {
                    operators.Push(token);
                }

                //If the token is )
                else if (token.Equals(")"))
                {
                    if (operators.IsOnTop <String>("+") || operators.IsOnTop <String>("-"))
                    {
                        HandlePlusMinus(operators, values);
                    }

                    //At this point, only a "(" should be on top of the stack.
                    operators.Pop();

                    //If there is a value on the stack, pop it and hand it over to the int handling function
                    if (values.Count > 0)
                    {
                        if (!HandleDouble(values.Pop(), operators, values))
                        {
                            return(divideByZero);
                        }
                    }
                }
            }

            //Operator and value stack checking after the last token has been processed.
            //If there are no operators, there should be 1 value on the stack - the result.
            //If there is an operator, there should be only 1 and it should be a + or -.
            //If this is the case, there should be two values on the value stack to be processed.
            if (operators.Count == 0 && values.Count == 1)
            {
                return(values.Pop());
            }
            //Operators count should be 1 and values count should be 2
            else
            {
                HandlePlusMinus(operators, values);
                return(values.Pop());
            }
        }
 /// <inheritdoc />
 /// <summary>
 /// Constructs an EvaluationException with the given FormulaError.
 /// </summary>
 /// <param name="error">The error to store in this exception.</param>
 public EvaluationException(FormulaError error)
 {
     Error = error;
 }
Beispiel #11
0
        /// <summary>
        /// Evaluates this Formula, using the lookup delegate to determine the values of
        /// variables.  When a variable symbol v needs to be determined, it should be looked up
        /// via lookup(normalize(v)). (Here, normalize is the normalizer that was passed to
        /// the constructor.)
        ///
        /// For example, if L("x") is 2, L("X") is 4, and N is a method that converts all the letters
        /// in a string to upper case:
        ///
        /// new Formula("x+7", N, s => true).Evaluate(L) is 11
        /// new Formula("x+7").Evaluate(L) is 9
        ///
        /// Given a variable symbol as its parameter, lookup returns the variable's value
        /// (if it has one) or throws an ArgumentException (otherwise).
        ///
        /// If no undefined variables or divisions by zero are encountered when evaluating
        /// this Formula, the value is returned.  Otherwise, a FormulaError is returned.
        /// The Reason property of the FormulaError should have a meaningful explanation.
        ///
        /// This method should never throw an exception.
        /// </summary>
        public object Evaluate(Func <string, double> lookup)
        {
            // vinc: check if formula causes FormulaFormatException
            if (!ValidFormat)
            {
                return(formaterror);
            }
            /// vinc: check if it is Circular Dependency
            if (CircularDependency)
            {
                FormulaError error = new FormulaError("Circular Dependency");
                error.CircularDependency = true;
                return(error);
            }

            //Set up stacks and start distributing the tokens into them
            Stack <double> values    = new Stack <double>();
            Stack <string> operators = new Stack <string>();

            //Loop over the tokens
            foreach (string token in normalizedFormula)
            {
                //Integer/Variable handling:
                double number;
                bool   isDouble = false;
                if (IsVariable(token))
                {
                    try
                    {
                        number = lookup(token);
                    }
                    catch (ArgumentException)
                    {
                        return(new FormulaError("Variable " + token + " Does Not Exist"));
                    }
                    isDouble = true;
                }
                else
                {
                    isDouble = double.TryParse(token, out number);
                }

                //Double Handling
                if (isDouble)
                {
                    if (IsOnTop(operators, "*") || IsOnTop(operators, "/"))
                    {
                        try
                        {
                            values.Push(Arithmator(operators.Pop(), values.Pop(), number));
                        }
                        catch (DivideByZeroException)
                        {
                            return(new FormulaError("Divide by Zero"));
                        }
                    }
                    else
                    {
                        values.Push(number);
                    }
                }

                //+ or - Operator Handling
                if (token.Equals("+") || token.Equals("-"))
                {
                    if (IsOnTop(operators, "+") || IsOnTop(operators, "-"))
                    {
                        double b = values.Pop();
                        double a = values.Pop();
                        values.Push(Arithmator(operators.Pop(), a, b));
                    }
                    operators.Push(token);
                }

                //* or / operator handling
                if (token.Equals("*") || token.Equals("/"))
                {
                    operators.Push(token);
                }

                //Parenthisis handling
                if (token.Equals("("))
                {
                    operators.Push(token);
                }
                if (token.Equals(")"))
                {
                    if (IsOnTop(operators, "+") || IsOnTop(operators, "-"))
                    {
                        double b = values.Pop();
                        double a = values.Pop();
                        values.Push(Arithmator(operators.Pop(), a, b));
                        operators.Pop(); //Pop the openparen from the operators stack
                    }

                    if (IsOnTop(operators, "*") || IsOnTop(operators, "/"))
                    {
                        try
                        {
                            double b = values.Pop();
                            double a = values.Pop();
                            values.Push(Arithmator(operators.Pop(), a, b));
                        }
                        catch (DivideByZeroException)
                        {
                            return(new FormulaError("Error: Divide by Zero"));
                        }
                    }
                }
            }

            //End behavior
            if (operators.Count == 0)
            {
                return(values.Pop());
            }
            else
            {
                double b = values.Pop();
                double a = values.Pop();
                values.Push(Arithmator(operators.Pop(), a, b));
                return(values.Pop());
            }
        }