-1

I'm writing a calculator code that solves the input whats given in string. All is good, except when it gets a negative result in the parentheses it fails badly because two operations get next to each other:

1+2*(10-11) >> 1+2*(-1) >> 1+2*-1

So where *- is, it gets "" (nothing) in the BigDecimal's constructor. I know what's the problem, but how can I solve it?

import java.math.BigDecimal;
import java.util.ArrayList;

public class DoMath {

    public static void main(String[] args) {

        // Test equation goes here.
        String number = "95.3+43.23*(10-11.1)";
        System.out.println(doMath(number));
    }

    public static BigDecimal doMath(String input) {

        StringBuilder builtInput = new StringBuilder(input);
        StringBuilder help = new StringBuilder();
        // Check if there are parenthesis in the equation.
        boolean noParenthesis = true;
        for (int i = 0; i < builtInput.length(); i++) {
            if (builtInput.charAt(i) == 40) {
                noParenthesis = false;
                break;
            }
        }

        if (noParenthesis) {    // If there are no parenthesis, calculate the equation!
            return calculateAndConvert(builtInput);

        } else {            // If there are parenthesis, breakdown to simple equations!
            int parenthesePair = 0;
            // Start extracting characters from the builtInput variable.
            for (int i = 0; i < builtInput.length(); i++) {
                // Start where we find a parentheses opener.
                if (builtInput.charAt(i) == 40) {
                    parenthesePair = 1;
                    builtInput.deleteCharAt(i);
                    for (int j = i; j < builtInput.length(); j++) {
                        // If we find another opener, add one to parenthesePair variable.
                        if (builtInput.charAt(j) == 40) {
                            parenthesePair++;
                        }
                        // If we find a closer, subtract one from the given variable.
                        if (builtInput.charAt(j) == 41) {
                            parenthesePair--;
                        }
                        // If we have found the matching pair, delete it and break the for loop.
                        if (parenthesePair == 0) {
                            builtInput.deleteCharAt(j);
                            builtInput.insert(j, doMath(help.toString()));
                            break;
                        }
                        help.append(builtInput.charAt(j));
                        builtInput.deleteCharAt(j);
                        j--;
                    }
                    break;
                }
            }
        }
        System.out.println(builtInput);
        return doMath(builtInput.toString());
    }

    public static BigDecimal calculateAndConvert(StringBuilder input) {

        ArrayList<BigDecimal> listOfNumbers = new ArrayList<BigDecimal>();
        StringBuilder numBay = new StringBuilder();
        StringBuilder operations = new StringBuilder();
        // If the first character is -, the first number is negative.
        boolean firstIsNegative = false;
        if (input.charAt(0) == 45) {
            firstIsNegative = true;
            input.deleteCharAt(0);
        }

        // Converting to numbers.
        while (input.length() != 0) {
            // If the character is a number or a dot, put it in the numBay variable and delete the char.
            if (input.charAt(0) >= 48 && input.charAt(0) <= 57 || input.charAt(0) == 46) {
                numBay.append(input.charAt(0));

                // If the character is not a number, put it in the operations variable
                // and save the number in the list (not operator characters are filtered)
            } else {
                listOfNumbers.add(new BigDecimal(numBay.toString()));
                numBay.setLength(0);
                operations.append(input.charAt(0));
            }
            // Delete the character.
            input.deleteCharAt(0);
        }
        listOfNumbers.add(new BigDecimal(numBay.toString()));

        // Setting first number to negative if it's needed.
        if (firstIsNegative) {
            listOfNumbers.set(0, listOfNumbers.get(0).negate());
        }

        // Calculate the result from the list and operations and return it.
        return calculate(listOfNumbers, operations);
    }

    public static BigDecimal calculate(ArrayList<BigDecimal> list, StringBuilder ops) {

        BigDecimal momentaryResult;
        // Check for a multiply operation - if there is one, solve it.
        for (int i = 0; i < ops.length(); i++) {
            if (ops.charAt(i) == 42) {
                momentaryResult = list.get(i).multiply(list.get(i + 1));
                list.remove(i);
                list.set(i, momentaryResult);
                ops.deleteCharAt(i);
                i--;
            }
        }
        // Check for a divide operation - if there is one, solve it.
        for (int i = 0; i < ops.length(); i++) {
            if (ops.charAt(i) == 47) {
                momentaryResult = list.get(i).divide(list.get(i + 1));
                list.remove(i);
                list.set(i, momentaryResult);
                ops.deleteCharAt(i);
                i--;
            }
        }
        // Check for a subtract operation - if there is one, solve it.
        for (int i = 0; i < ops.length(); i++) {
            if (ops.charAt(i) == 45) {
                momentaryResult = list.get(i).subtract(list.get(i + 1));
                list.remove(i);
                list.set(i, momentaryResult);
                ops.deleteCharAt(i);
                i--;
            }
        }
        // Check for a plus operation - if there is one, solve it.
        for (int i = 0; i < ops.length(); i++) {
            if (ops.charAt(i) == 43) {
                momentaryResult = list.get(i).add(list.get(i + 1));
                list.remove(i);
                list.set(i, momentaryResult);
                ops.deleteCharAt(i);
                i--;
            }
        }

        // Return with the one remaining number that represents the result.
        return list.get(0);
    }
}

Edit: or would it be easier to write a new code with a different algorithm...?

Tiny
  • 27,221
  • 105
  • 339
  • 599
  • 6
    For the sake of anyone that ever has to look at your code - please don't use character codes, use the actual characters in single quotes instead - e.g. `'-'` instead of `45`. – Bernhard Barker Feb 02 '14 at 03:14
  • I would like to add to @Dukeling's comment, that not only for other's that work on your code, but for the sake of yourself as well when you go back and re-read this code later you will thank yourself for using the actual characters. – mdewitt Feb 02 '14 at 03:22
  • 1
    You don´t need a for loop to check if there is a Parenthesis in your string. Just use: string.contains("("); – HectorLector Feb 02 '14 at 03:24
  • yes will change them.. don't even know why I used ascii.. Thanks for the tip and for the string.contains()! – Barnabas Lesti Feb 02 '14 at 03:36
  • just a suggestion if you want to improve your code - you might have a look at the [interpreter-pattern](http://en.wikipedia.org/wiki/Interpreter_pattern). Although I've not used it in that example to calculate something but to evaluate if a statement is true, the technique presented in [this post](http://stackoverflow.com/questions/20763189/creating-a-simple-rule-engine-in-java/20892587#20892587) could be applied for your scenario – Roman Vottner Feb 02 '14 at 03:51
  • [ANTLR4](http://www.antlr.org/) is your friend! –  Feb 02 '14 at 03:56
  • Thank you very much! Will check these out! – Barnabas Lesti Feb 02 '14 at 10:22

1 Answers1

0

I would post this as a comment to your question, but I do not have the required reputation to do so.

Anyway, since you have already recognized that the bug is the "operator" *- couldn't you make a method that would fix this problem by replacing the plus operator immediately before by a minus? Like this:

1+2*-1 >>> 1-2*1

If you want I can write you the code. But maybe it will be easier for you to adapt a solution like this in your code that is already working.

Edit - 1:

Obviously, the code should also treat the following cases:

1-2*-1 >>> 1+2*1

2*-1 >>> -2*1

Edit - 2:

Here is the code I managed to make. Let me know if you find any errors.

public int countChar(String str, char chr) {
    int count = 0;
    for (int k = 0; k < str.length(); k++) {
        if (str.charAt(k) == chr)
            count++;
    }
    return count;
}

public String fixBug(String eq) {
    boolean hasBug = eq.contains("*-");
    if (hasBug) {
        String subeq;
        int indbug, indp, indm;
        eq = eq.replace("*-", "@");
        int N = countChar(eq, '@');
        for (int k = N; k > 0; k--) {
            indbug = eq.indexOf('@');
            subeq = eq.substring(0, indbug);
            indp = subeq.lastIndexOf('+');
            indm = subeq.lastIndexOf('-');
            if (indp == -1 && indm == -1) {
                eq = "-" + eq;
            } else if (indp > indm) {
                eq = eq.substring(0, indp) + '-' + eq.substring(indp + 1);
            } else {
                eq = eq.substring(0, indm) + '+' + eq.substring(indm + 1);
            }
        }
        eq = eq.replace("@", "*");
    }
    return eq;
}
jcmonteiro
  • 293
  • 2
  • 16