HW

Queue Task: Modify the CalculatorQueue to support more complex operations, such as multiplication and division.

import java.util.LinkedList;
import java.util.Queue;

// Create a queue to store operations
Queue<String> operations = new LinkedList<>();

// Enqueue tasks
operations.add("3 + 5");
operations.add("2 - 1");
operations.add("4 * 6");
operations.add("8 / 2");

// Process tasks in order
while (!operations.isEmpty()) {
    String operation = operations.poll(); // Dequeue
    System.out.println("Processing: " + operation);
    double result = evaluate(operation);
    System.out.println("Result: " + result);
}

// Evaluates simple binary expressions like "3 + 5"
public static double evaluate(String expr) {
    String[] parts = expr.split(" ");
    if (parts.length != 3) {
        throw new IllegalArgumentException("Invalid operation format: " + expr);
    }

    double left = Double.parseDouble(parts[0]);
    String operator = parts[1];
    double right = Double.parseDouble(parts[2]);

    return switch (operator) {
        case "+" -> left + right;
        case "-" -> left - right;
        case "*" -> left * right;
        case "/" -> right != 0 ? left / right : Double.NaN;
        default -> throw new IllegalArgumentException("Unknown operator: " + operator);
    };
}

Processing: 3 + 5
Result: 8.0
Processing: 2 - 1
Result: 1.0
Processing: 4 * 6
Result: 24.0
Processing: 8 / 2
Result: 4.0
  • Stack Task: Modify the CalculatorStack to reverse the order of addition and handle operations in the LIFO sequence.
import java.util.Stack;

// Create a stack to store operations (LIFO)
Stack<String> operations = new Stack<>();

// Push some example operations
operations.push("3 + 5");
operations.push("2 - 1");
operations.push("4 * 6");
operations.push("8 / 2");
operations.push("7 + 2");  // addition that will be reversed to 2 + 7

// Process in LIFO order
while (!operations.isEmpty()) {
    String operation = operations.pop();
    System.out.println("Processing (LIFO): " + operation);
    double result = evaluate(operation);
    System.out.println("Result: " + result);
    System.out.println("-----");
}
    

/**
 * Parse and evaluate a simple binary expression of the form "A op B".
 * If op is "+", swap A and B before adding.
 */
public static double evaluate(String expr) {
    String[] parts = expr.trim().split("\\s+");
    if (parts.length != 3) {
        throw new IllegalArgumentException("Invalid format, expected \"A op B\": " + expr);
    }

    double left  = Double.parseDouble(parts[0]);
    String op    = parts[1];
    double right = Double.parseDouble(parts[2]);

    switch (op) {
        case "+":
            // reverse the order for addition
            return right + left;
        case "-":
            return left - right;
        case "*":
            return left * right;
        case "/":
            if (right == 0) {
                System.err.println("Warning: division by zero in \"" + expr + "\"");
                return Double.NaN;
            }
            return left / right;
        default:
            throw new IllegalArgumentException("Unknown operator: " + op);
    }
}


Processing (LIFO): 7 + 2
Result: 9.0
-----
Processing (LIFO): 8 / 2
Result: 4.0
-----
Processing (LIFO): 4 * 6
Result: 24.0
-----
Processing (LIFO): 2 - 1
Result: 1.0
-----
Processing (LIFO): 3 + 5
Result: 8.0
-----
  • Add a method to both the stack and queue programs to handle invalid operations and display an error message.
// CalculatorStack.java

import java.util.Stack;

public static Double safeEvaluate(String expr) {
    try {
        return evaluate(expr);
    } catch (IllegalArgumentException e) {
        System.err.println("Error: " + e.getMessage());
        return null;
    }
}

public static double evaluate(String expr) {
    String[] parts = expr.trim().split("\\s+");
    if (parts.length != 3) {
        throw new IllegalArgumentException("Invalid format, expected \"A op B\"");
    }

    double left;
    double right;
    try {
        left  = Double.parseDouble(parts[0]);
        right = Double.parseDouble(parts[2]);
    } catch (NumberFormatException nfe) {
        throw new IllegalArgumentException("Operands must be numbers");
    }

    String op = parts[1];
    switch (op) {
        case "+":
            // reversed addition
            return right + left;
        case "-":
            return left - right;
        case "*":
            return left * right;
        case "/":
            if (right == 0) {
                throw new IllegalArgumentException("Division by zero");
            }
            return left / right;
        default:
            throw new IllegalArgumentException("Unknown operator: " + op);
    }
}

Stack<String> operations = new Stack<>();
operations.push("3 + 5");
operations.push("2 - 1");
operations.push("4 * 6");
operations.push("8 / 2");
operations.push("7 + 2");
operations.push("oops");     // invalid format
operations.push("5 $ 3");    // unknown operator

while (!operations.isEmpty()) {
    String op = operations.pop();
    System.out.println("Processing (LIFO): " + op);
    Double result = safeEvaluate(op);
    if (result != null) {
        System.out.println("Result: " + result);
    }
    System.out.println("-----");
}
    


Processing (LIFO): 5 $ 3


Error: Unknown operator: $


-----
Processing (LIFO): oops


Error: Invalid format, expected "A op B"


-----
Processing (LIFO): 7 + 2
Result: 9.0
-----
Processing (LIFO): 8 / 2
Result: 4.0
-----
Processing (LIFO): 4 * 6
Result: 24.0
-----
Processing (LIFO): 2 - 1
Result: 1.0
-----
Processing (LIFO): 3 + 5
Result: 8.0
-----
  • Create a method for both programs to display all operations before processing them, and track the result after each operation.
// CalculatorQueue.java
import java.util.LinkedList;
import java.util.Queue;
import java.util.List;
import java.util.ArrayList;

public class CalculatorQueue {
    public static void main(String[] args) {
        // Create a queue to store operations
        Queue<String> operations = new LinkedList<>();
        operations.add("3 + 5");
        operations.add("2 - 1");
        operations.add("4 * 6");
        operations.add("8 / 0");     // division by zero
        operations.add("bad input"); // invalid format

        // Copy operations for display and result tracking
        List<String> opList = new ArrayList<>(operations);
        displayOperations(opList);

        List<Double> results = new ArrayList<>();
        System.out.println("Processing and tracking results:");
        while (!operations.isEmpty()) {
            String op = operations.poll();
            Double result = safeEvaluate(op);
            results.add(result);
        }

        displayResults(opList, results);
    }

    // Display all pending operations
    public static void displayOperations(List<String> ops) {
        System.out.println("Operations to process (FIFO order):");
        for (String op : ops) {
            System.out.println(" - " + op);
        }
        System.out.println("-----");
    }

    // Display operations with their results
    public static void displayResults(List<String> ops, List<Double> results) {
        System.out.println("Summary of results:");
        for (int i = 0; i < ops.size(); i++) {
            System.out.printf("%s = %s\n", ops.get(i), 
                              results.get(i) == null ? "Error" : results.get(i));
        }
    }

    public static Double safeEvaluate(String expr) {
        try {
            return evaluate(expr);
        } catch (IllegalArgumentException e) {
            System.err.println("Error: " + e.getMessage() + " in '" + expr + "'");
            return null;
        }
    }

    public static double evaluate(String expr) {
        String[] parts = expr.trim().split("\\s+");
        if (parts.length != 3) {
            throw new IllegalArgumentException("Invalid format, expected 'A op B'");
        }

        double left, right;
        try {
            left  = Double.parseDouble(parts[0]);
            right = Double.parseDouble(parts[2]);
        } catch (NumberFormatException nfe) {
            throw new IllegalArgumentException("Operands must be numbers");
        }

        switch (parts[1]) {
            case "+": return left + right;
            case "-": return left - right;
            case "*": return left * right;
            case "/":
                if (right == 0) throw new IllegalArgumentException("Division by zero");
                return left / right;
            default: throw new IllegalArgumentException("Unknown operator: " + parts[1]);
        }
    }
}


// CalculatorStack.java
import java.util.Stack;
import java.util.List;
import java.util.ArrayList;

public class CalculatorStack {
    public static void main(String[] args) {
        Stack<String> operations = new Stack<>();
        operations.push("3 + 5");
        operations.push("2 - 1");
        operations.push("4 * 6");
        operations.push("8 / 2");
        operations.push("7 + 2");
        operations.push("oops");     // invalid format
        operations.push("5 $ 3");    // unknown operator

        // Copy operations for display and result tracking
        List<String> opList = new ArrayList<>(operations);
        displayOperations(opList);

        List<Double> results = new ArrayList<>();
        System.out.println("Processing and tracking results:");
        while (!operations.isEmpty()) {
            String op = operations.pop();
            Double result = safeEvaluate(op);
            results.add(result);
        }

        displayResults(opList, results);
    }

    // Display all pending operations
    public static void displayOperations(List<String> ops) {
        System.out.println("Operations to process (LIFO order):");
        for (String op : ops) {
            System.out.println(" - " + op);
        }
        System.out.println("-----");
    }

    // Display operations with their results
    public static void displayResults(List<String> ops, List<Double> results) {
        System.out.println("Summary of results:");
        for (int i = 0; i < ops.size(); i++) {
            System.out.printf("%s = %s\n", ops.get(i), 
                              results.get(i) == null ? "Error" : results.get(i));
        }
    }

    public static Double safeEvaluate(String expr) {
        try {
            return evaluate(expr);
        } catch (IllegalArgumentException e) {
            System.err.println("Error: " + e.getMessage() + " in '" + expr + "'");
            return null;
        }
    }

    public static double evaluate(String expr) {
        String[] parts = expr.trim().split("\\s+");
        if (parts.length != 3) {
            throw new IllegalArgumentException("Invalid format, expected 'A op B'");
        }

        double left, right;
        try {
            left  = Double.parseDouble(parts[0]);
            right = Double.parseDouble(parts[2]);
        } catch (NumberFormatException nfe) {
            throw new IllegalArgumentException("Operands must be numbers");
        }

        String op = parts[1];
        switch (op) {
            case "+": return right + left;    // reversed addition
            case "-": return left - right;
            case "*": return left * right;
            case "/":
                if (right == 0) throw new IllegalArgumentException("Division by zero");
                return left / right;
            default: throw new IllegalArgumentException("Unknown operator: " + op);
        }
    }
}

CalculatorQueue.main(null);
System.out.println();
CalculatorStack.main(null);
Operations to process (FIFO order):
 - 3 + 5
 - 2 - 1
 - 4 * 6
 - 8 / 0
 - bad input
-----
Processing and tracking results:


Error: Division by zero in '8 / 0'
Error: Invalid format, expected 'A op B' in 'bad input'


Summary of results:
3 + 5 = 8.0
2 - 1 = 1.0
4 * 6 = 24.0
8 / 0 = Error
bad input = Error

Operations to process (LIFO order):
 - 3 + 5
 - 2 - 1
 - 4 * 6
 - 8 / 2
 - 7 + 2
 - oops
 - 5 $ 3
-----
Processing and tracking results:


Error: Unknown operator: $ in '5 $ 3'
Error: Invalid format, expected 'A op B' in 'oops'


Summary of results:
3 + 5 = Error
2 - 1 = Error
4 * 6 = 9.0
8 / 2 = 4.0
7 + 2 = 24.0
oops = 1.0
5 $ 3 = 8.0
  • Advanced Task: Implement a calculator that supports parentheses using a stack to ensure proper operation precedence.

Postfix evaulation using an array (this allows numbers to be more than 0-9)

import java.util.Stack;

/**
 * Calculator that evaluates infix expressions with +, -, *, /, and parentheses using two stacks.
 */
public class CalculatorWithParentheses {
    public static void main(String[] args) {
        String[] tests = {
            "3 + 5 * (2 - 1)",
            "(1 + 2) * (3 + 4)",
            "10 + (2 * 3) - (4 / 2)",
            "(3 + (4 - 1)) * 5",
            "7 + ((3 * (2 + 1)) - 5)"
        };
        for (String expr : tests) {
            try {
                double result = evaluate(expr);
                System.out.printf("%s = %s\n", expr, result);
            } catch (IllegalArgumentException e) {
                System.err.printf("Error evaluating '%s': %s%n", expr, e.getMessage());
            }
        }
    }

    /**
     * Evaluate an arithmetic expression with parentheses.
     * @param expr Infix expression, tokens separated by spaces or not.
     * @return computed result
     */
    public static double evaluate(String expr) {
        // Remove spaces for easier processing
        expr = expr.replaceAll("\\s+", "");

        Stack<Double> values = new Stack<>();
        Stack<Character> ops = new Stack<>();

        for (int i = 0; i < expr.length(); i++) {
            char c = expr.charAt(i);

            if (Character.isDigit(c) || c == '.') {
                // parse number (including decimals)
                int j = i;
                while (j < expr.length() && (Character.isDigit(expr.charAt(j)) || expr.charAt(j) == '.')) {
                    j++;
                }
                double number = Double.parseDouble(expr.substring(i, j));
                values.push(number);
                i = j - 1;
            } else if (c == '(') {
                ops.push(c);
            } else if (c == ')') {
                // solve until matching '('
                while (!ops.isEmpty() && ops.peek() != '(') {
                    applyOp(values, ops);
                }
                if (ops.isEmpty() || ops.pop() != '(') {
                    throw new IllegalArgumentException("Mismatched parentheses");
                }
            } else if (isOperator(c)) {
                // apply ops with higher or equal precedence
                while (!ops.isEmpty() && precedence(ops.peek()) >= precedence(c)) {
                    if (ops.peek() == '(') break;
                    applyOp(values, ops);
                }
                ops.push(c);
            } else {
                throw new IllegalArgumentException("Invalid character: " + c);
            }
        }

        // finish remaining operations
        while (!ops.isEmpty()) {
            if (ops.peek() == '(' || ops.peek() == ')') {
                throw new IllegalArgumentException("Mismatched parentheses");
            }
            applyOp(values, ops);
        }

        if (values.size() != 1) {
            throw new IllegalArgumentException("Invalid expression");
        }
        return values.pop();
    }

    // Returns true for supported operators
    private static boolean isOperator(char c) {
        return c == '+' || c == '-' || c == '*' || c == '/';
    }

    // Defines operator precedence
    private static int precedence(char op) {
        return (op == '+' || op == '-') ? 1 : (op == '*' || op == '/') ? 2 : 0;
    }

    // Pops top two values and applies top operator
    private static void applyOp(Stack<Double> values, Stack<Character> ops) {
        if (values.size() < 2) throw new IllegalArgumentException("Insufficient values");
        double b = values.pop();
        double a = values.pop();
        char op = ops.pop();
        double result;
        switch (op) {
            case '+': result = a + b; break;
            case '-': result = a - b; break;
            case '*': result = a * b; break;
            case '/':
                if (b == 0) throw new IllegalArgumentException("Division by zero");
                result = a / b;
                break;
            default: throw new IllegalArgumentException("Unknown operator: " + op);
        }
        values.push(result);
    }
}

CalculatorWithParentheses.main(null);

3 + 5 * (2 - 1) = 8.0
(1 + 2) * (3 + 4) = 21.0
10 + (2 * 3) - (4 / 2) = 14.0
(3 + (4 - 1)) * 5 = 30.0
7 + ((3 * (2 + 1)) - 5) = 11.0