# Kwuxy's solution

## to Wordy in the Java Track

Published at Nov 12 2019 · 0 comments
Instructions
Test suite
Solution

Parse and evaluate simple math word problems returning the answer as an integer.

## Iteration 0 — Numbers

Problems with no operations simply evaluate to the number given.

What is 5?

Evaluates to 5.

## Iteration 1 — Addition

Add two numbers together.

What is 5 plus 13?

Evaluates to 18.

Handle large numbers and negative numbers.

## Iteration 2 — Subtraction, Multiplication and Division

Now, perform the other three operations.

What is 7 minus 5?

2

What is 6 multiplied by 4?

24

What is 25 divided by 5?

5

## Iteration 3 — Multiple Operations

Handle a set of operations, in sequence.

Since these are verbal word problems, evaluate the expression from left-to-right, ignoring the typical order of operations.

What is 5 plus 13 plus 6?

24

What is 3 plus 2 multiplied by 3?

15 (i.e. not 9)

## Iteration 4 — Errors

The parser should reject:

• Unsupported operations ("What is 52 cubed?")
• Non-math questions ("Who is the President of the United States")
• Word problems with invalid syntax ("What is 1 plus plus 2?")

## Bonus — Exponentials

If you'd like, handle exponentials.

What is 2 raised to the 5th power?

32

## Setup

Go through the setup instructions for Java to install the necessary dependencies:

https://exercism.io/tracks/java/installation

# Running the tests

You can run all the tests for an exercise by entering the following in your terminal:

``````\$ gradle test
``````

Use `gradlew.bat` if you're on Windows

In the test suites all tests but the first have been skipped.

Once you get a test passing, you can enable the next one by removing the `@Ignore("Remove to run test")` annotation.

## Source

Inspired by one of the generated questions in the Extreme Startup game. https://github.com/rchatley/extreme_startup

## Submitting Incomplete Solutions

It's possible to submit an incomplete solution so you can see how others have completed the exercise.

### WordProblemSolverTest.java

``````import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.Before;
import org.junit.rules.ExpectedException;

import static org.junit.Assert.assertEquals;

public class WordProblemSolverTest {

@Rule
public ExpectedException expectedException = ExpectedException.none();

WordProblemSolver solver;

@Before
public void setup() {
solver = new WordProblemSolver();
}

@Test
public void testJustANumber() {
assertEquals(5, solver.solve("What is 5?"));
}

@Ignore("Remove to run test")
@Test
public void testSingleAddition1() {
assertEquals(2, solver.solve("What is 1 plus 1?"));
}

@Ignore("Remove to run test")
@Test
public void testSingleAddition2() {
assertEquals(55, solver.solve("What is 53 plus 2?"));
}

@Ignore("Remove to run test")
@Test
public void testSingleAdditionWithNegativeNumbers() {
assertEquals(-11, solver.solve("What is -1 plus -10?"));
}

@Ignore("Remove to run test")
@Test
public void testSingleAdditionOfLargeNumbers() {
assertEquals(45801, solver.solve("What is 123 plus 45678?"));
}

@Ignore("Remove to run test")
@Test
public void testSingleSubtraction() {
assertEquals(16, solver.solve("What is 4 minus -12?"));
}

@Ignore("Remove to run test")
@Test
public void testSingleMultiplication() {
assertEquals(-75, solver.solve("What is -3 multiplied by 25?"));
}

@Ignore("Remove to run test")
@Test
public void testSingleDivision() {
assertEquals(-11, solver.solve("What is 33 divided by -3?"));
}

@Ignore("Remove to run test")
@Test
public void testMultipleAdditions() {
assertEquals(3, solver.solve("What is 1 plus 1 plus 1?"));
}

@Ignore("Remove to run test")
@Test
public void testAdditionThenSubtraction() {
assertEquals(8, solver.solve("What is 1 plus 5 minus -2?"));
}

@Ignore("Remove to run test")
@Test
public void testMultipleSubtractions() {
assertEquals(3, solver.solve("What is 20 minus 4 minus 13?"));
}

@Ignore("Remove to run test")
@Test
public void testSubtractionThenAddition() {
assertEquals(14, solver.solve("What is 17 minus 6 plus 3?"));
}

@Ignore("Remove to run test")
@Test
public void testMultipleMultiplications() {
assertEquals(-12, solver.solve("What is 2 multiplied by -2 multiplied by 3?"));
}

@Ignore("Remove to run test")
@Test
public void testAdditionThenMultiplication() {
assertEquals(-8, solver.solve("What is -3 plus 7 multiplied by -2?"));
}

@Ignore("Remove to run test")
@Test
public void testMultipleDivisions() {
assertEquals(2, solver.solve("What is -12 divided by 2 divided by -3?"));
}

@Ignore("Remove to run test")
@Test
public void testUnknownOperation() {
expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage("I'm sorry, I don't understand the question!");

solver.solve("What is 52 cubed?");
}

@Ignore("Remove to run test")
@Test
public void testNonMathQuestion() {
expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage("I'm sorry, I don't understand the question!");

// See https://en.wikipedia.org/wiki/President_of_the_United_States if you really need to know!
solver.solve("Who is the President of the United States?");
}

@Ignore("Remove to run test")
@Test
public void testMissingAnOperand() {
expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage("I'm sorry, I don't understand the question!");

solver.solve("What is 1 plus?");
}

@Ignore("Remove to run test")
@Test
public void testNoOperandsOrOperators() {
expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage("I'm sorry, I don't understand the question!");

solver.solve("What is?");
}

@Ignore("Remove to run test")
@Test
public void testTwoOperationsInARow() {
expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage("I'm sorry, I don't understand the question!");

solver.solve("What is 1 plus plus 2?");
}

@Ignore("Remove to run test")
@Test
public void testTwoNumbersAfterOperation() {
expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage("I'm sorry, I don't understand the question!");

solver.solve("What is 1 plus 2 1?");
}

@Ignore("Remove to run test")
@Test
public void testPostfixNotation() {
expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage("I'm sorry, I don't understand the question!");

solver.solve("What is 1 2 plus?");
}

@Ignore("Remove to run test")
@Test
public void testPrefixNotation() {
expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage("I'm sorry, I don't understand the question!");

solver.solve("What is plus 1 2?");
}
}``````

### src/main/java/Calculator.java

``````import java.util.LinkedList;
import java.util.Map;
import java.util.Queue;
import java.util.Stack;
import java.util.stream.Collectors;
import java.util.stream.Stream;

class Calculator {
private final Map<String, Integer> precedence = Stream.of(new String[][] {
{"+", "1"},
{"-", "1"},
{"*", "2"},
{"/", "2"},
}).collect(Collectors.toMap(data -> data[0], data -> Integer.valueOf(data[1])));

private Stack<Integer> operands;

int calculate(String rpnExpression) {
operands = new Stack<>();
for (String element : rpnExpression.split(" ")) {
if(isInteger(element)) {
continue;
}
processOperation(element);
}
if(operands.size() > 1) {
throw new IllegalArgumentException();
}

return operands.pop();
}

private void processOperation(String operator) {
int operand;

switch(operator) {
case "+":
operands.push(operands.pop() + operands.pop());
break;
case "-":
operand = operands.pop();
operands.push(operands.pop() - operand);
break;
case "*":
operands.push(operands.pop() * operands.pop());
break;
case "/":
operand = operands.pop();
operands.push(operands.pop() / operand);
}
}

private int getOperandValue(String element) {
return Integer.valueOf(element);
}

String convertToRpnExpression(String arithmeticExpression) {
final String[] elements = arithmeticExpression.split(" ");
final Stack<String> operators = new Stack<>();
final Queue<String> output = new LinkedList<>();
boolean lastCharacterWasNumber = false;

for (String element : elements) {
if(isInteger(element)) {
if(lastCharacterWasNumber) throw new IllegalArgumentException();

lastCharacterWasNumber = true;
continue;
}

lastCharacterWasNumber = false;

if(!isSupported(element)) {
throw new IllegalArgumentException();
}

while(!operators.empty()) {
String operator = operators.peek();
if(precedence.get(operator) > precedence.get(element)) {
break;
}

}

}

while(!operators.empty()) {
}

return String.join(" ", output);
}

private boolean isSupported(String element) {
return precedence.containsKey(element);
}

private boolean isInteger(String element) {
return element.matches("[-+]?\\p{Digit}+");
}
}``````

### src/main/java/WordProblemSolver.java

``````import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;

class WordProblemSolver {

private String phrase;
private final Map<String, String> operatorsMapping = Stream.of(new String[][] {
{"minus", "-"},
{"plus", "+"},
{"multiplied by", "*"},
{"divided by", "/"},
}).collect(Collectors.toMap(data -> data[0], data -> data[1]));

public int solve(String phrase) {
this.phrase = phrase;
final Calculator calculator = new Calculator();

final String arithmeticExpression = convertPhraseToArithmeticExpression();
try {
final String rpnExpression = calculator.convertToRpnExpression(arithmeticExpression);
if(rpnExpression.length() == 0) {
throwError();
}

return calculator.calculate(rpnExpression);
} catch (Exception e) {
throwError();
return 0;
}
}

private void throwError() {
throw new IllegalArgumentException("I'm sorry, I don't understand the question!");
}

private String convertPhraseToArithmeticExpression() {
cutPhraseQuestionFormula();
replaceArithmeticOperators();
return phrase;
}

private void replaceArithmeticOperators() {
for (Map.Entry<String, String> entry : operatorsMapping.entrySet()) {
phrase = phrase.replaceAll(entry.getKey(), entry.getValue());
}
}

private void cutPhraseQuestionFormula() {
if(phrase.length() > 8) {
phrase = phrase.substring(8, phrase.length() - 1);
}
}
}``````