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