Cornelius Eanes's Programming Portal
Assignments (Project 4)
[Project 4] Calculator

Goal: Create a simple calculator. It must be able to add, subtract, multiply, divide, raise to a power, and modulate.

Calculator.java
/// Name: Cornelius Eanes
/// Period: 5
/// Program Name: Calculator
/// File Name: Calculator.java
/// Date Finished: May 12, 2016
import java.math.BigDecimal;
import java.math.MathContext;
import java.util.Scanner;

public class Calculator {

    public static void main(String[] args) {

        Scanner userIn = new Scanner(System.in);

        System.out.println("Welcome to Cornelius' Calculator!");
        System.out.println("Type \"oper\" for a list of valid operations.");
        System.out.println("Type \"const\" for a list of valid constants.");
        System.out.println("Type \"prec\" to toggle the amount of precision.");
        System.out.println("Type \"quit\" to quit.");
        String line;
        do {
            System.out.print("\n> ");
            line = userIn.nextLine();
            try {
                if ("oper".equals(line)) {
                    listOperations();
                } else if ("const".equals(line)) {
                    listConstants();
                } else if ("prec".equals(line)) {
                    changePrecision();
                } else if (!"quit".equals(line)) {
                    BigDecimal ans = Operation.parseAndOperate(line);
                    System.out.println("ANS: " + ans);
                    lastAnswer = ans;
                }
            } catch (Exception e) {
                System.out.println("ERROR: " + e.getMessage());
            }
        } while (!"quit".equals(line));

    }

    private static void listOperations() {
        for (Operation op : Operation.values()) {
            System.out.println(op.name() + " (" + op.symbol + ")");
        }
    }

    private static void listConstants() {
        System.out.println("E - Euler's constant");
        System.out.println("PI - Circle circumference-to-diameter ratio");
        System.out.print("ANS - Last calculated answer");
        if (lastAnswer != null) {
            System.out.println(" (" + lastAnswer + ")");
        } else {
            System.out.println();
        }
    }

    private static void changePrecision() {
        if (ctx.equals(MathContext.DECIMAL32)) {
            ctx = MathContext.DECIMAL64;
        } else if (ctx.equals(MathContext.DECIMAL64)) {
            ctx = MathContext.UNLIMITED;
        } else {
            ctx = MathContext.DECIMAL32;
        }
        System.out.println("Precision changed to " + ctx.getPrecision());
    }

    private static BigDecimal lastAnswer = null;
    private static MathContext ctx = MathContext.DECIMAL32;

    public static BigDecimal getLastAnswer() {
        return lastAnswer;
    }

    public static MathContext getMathContext() {
        return ctx;
    }

}
Operation.java
/// Name: Cornelius Eanes
/// Period: 5
/// Program Name: Calculator
/// File Name: Operation.java
/// Date Finished: May 12, 2016
import java.math.BigDecimal;
import java.util.NoSuchElementException;
import java.util.Scanner;

public enum Operation {

    ADD('+') {
        @Override
        public BigDecimal operate(BigDecimal... nums) {
            if (nums.length == 2) {
                return nums[0].add(nums[1]);
            }
            return invalidNumArgs(2, nums);
        }
    },
    SUBTRACT('-') {
        @Override
        public BigDecimal operate(BigDecimal... nums) throws IllegalArgumentException {
            if (nums.length == 2) {
                return nums[0].subtract(nums[1]);
            }
            return invalidNumArgs(2, nums);
        }
    },
    MULTIPLY('*') {
        @Override
        public BigDecimal operate(BigDecimal... nums) throws IllegalArgumentException {
            if (nums.length == 2) {
                return nums[0].multiply(nums[1]);
            }
            return invalidNumArgs(2, nums);
        }
    },
    DIVIDE('/') {
        @Override
        public BigDecimal operate(BigDecimal... nums) throws IllegalArgumentException {
            if (nums.length == 2) {
                return nums[0].divide(nums[1], BigDecimal.ROUND_HALF_UP);
            }
            return invalidNumArgs(2, nums);
        }
    },
    MOD('%') {
        @Override
        public BigDecimal operate(BigDecimal... nums) throws IllegalArgumentException {
            if (nums.length == 2) {
                return nums[0].remainder(nums[1]);
            }
            return invalidNumArgs(2, nums);
        }
    },
    EXPONENT('^') {
        @Override
        public BigDecimal operate(BigDecimal... nums) throws IllegalArgumentException {
            if (nums.length == 2) {
                return nums[0].pow(nums[1].intValue());
            }
            return invalidNumArgs(2, nums);
        }
    };

    public final char symbol;

    Operation(char symbol) {
        this.symbol = symbol;
    }

    public abstract BigDecimal operate(BigDecimal... nums) throws IllegalArgumentException;

    static BigDecimal invalidNumArgs(int required, BigDecimal[] nums) {
        throw new IllegalArgumentException("Requires " + required + " arguments, found " + nums.length);
    }

    public static Operation getFromSymbol(char c) {
        for (Operation op : Operation.values()) {
            if (op.symbol == c) {
                return op;
            }
        }
        return null;
    }

    public static BigDecimal parseAndOperate(String s) {
        Scanner scanner = new Scanner(s);
        String o1 = null, o2 = null, o3;
        BigDecimal n1 = null, n2 = null;
        Operation op = null;
        try {
            o1 = scanner.next();
            o2 = scanner.next();
            o3 = scanner.next();
            n1 = parseDecimal(o1);
            op = getFromSymbol(o2.charAt(0));
            if (op == null) {
                throw new IllegalArgumentException("Invalid operand symbol: " + o2);
            }
            n2 = parseDecimal(o3);
            return op.operate(n1, n2).round(Calculator.getMathContext());
        } catch (NoSuchElementException e) {
            throw new RuntimeException("Requires 3 arguments: [num] [operand] [num]");
        } catch (NumberFormatException e) {
            throw new RuntimeException("Invalid number: " + (n1 == null ? o1 : o2));
        }
    }

    private static BigDecimal
            BD_E = new BigDecimal(Math.E),
            BD_PI = new BigDecimal(Math.PI);

    private static BigDecimal parseDecimal(String s) throws IllegalArgumentException {
        if ("e".equalsIgnoreCase(s)) {
            return BD_E;
        } else if ("pi".equalsIgnoreCase(s)) {
            return BD_PI;
        } else if ("ans".equalsIgnoreCase(s)) {
            return Calculator.getLastAnswer();
        }
        try {
            return new BigDecimal(s);
        } catch (NumberFormatException e) {
            throw new IllegalArgumentException("Not a valid constant nor number: " + s);
        }
    }

}

Output