diff --git a/Mock/.gitignore b/Mock/.gitignore new file mode 100644 index 0000000..345e61a --- /dev/null +++ b/Mock/.gitignore @@ -0,0 +1,49 @@ +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff: +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/dictionaries + +# Sensitive or high-churn files: +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.xml +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml + +# Gradle: +.idea/**/gradle.xml +.idea/**/libraries + +# CMake +cmake-build-debug/ + +# Mongo Explorer plugin: +.idea/**/mongoSettings.xml + +## File-based project format: +*.iws + +## Plugin-specific files: + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties diff --git a/Mock/.idea/compiler.xml b/Mock/.idea/compiler.xml new file mode 100644 index 0000000..691ff71 --- /dev/null +++ b/Mock/.idea/compiler.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Mock/.idea/misc.xml b/Mock/.idea/misc.xml new file mode 100644 index 0000000..9896aeb --- /dev/null +++ b/Mock/.idea/misc.xml @@ -0,0 +1,11 @@ + + + + + + + \ No newline at end of file diff --git a/Mock/.idea/modules.xml b/Mock/.idea/modules.xml new file mode 100644 index 0000000..10cf8c4 --- /dev/null +++ b/Mock/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/Mock/Mock.iml b/Mock/Mock.iml new file mode 100644 index 0000000..690021f --- /dev/null +++ b/Mock/Mock.iml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Mock/pom.xml b/Mock/pom.xml new file mode 100644 index 0000000..84bb5ee --- /dev/null +++ b/Mock/pom.xml @@ -0,0 +1,47 @@ + + + 4.0.0 + + ru.spbau.mit.kazakov + Mock + 1.0-SNAPSHOT + + + + org.apache.maven.plugins + maven-compiler-plugin + + 1.8 + 1.8 + + + + + + + + junit + junit + 4.8 + + + org.jetbrains + annotations + 13.0 + + + com.google.guava + guava + r05 + + + org.mockito + mockito-all + 1.9.5 + test + + + + \ No newline at end of file diff --git a/Mock/src/main/java/ru/mit/spbau/kazakov/arithmetic/ArithmeticCalculator.java b/Mock/src/main/java/ru/mit/spbau/kazakov/arithmetic/ArithmeticCalculator.java new file mode 100644 index 0000000..b284499 --- /dev/null +++ b/Mock/src/main/java/ru/mit/spbau/kazakov/arithmetic/ArithmeticCalculator.java @@ -0,0 +1,26 @@ +package ru.mit.spbau.kazakov.arithmetic; + +import ru.mit.spbau.kazakov.exception.ArithmeticException; +import ru.mit.spbau.kazakov.util.Stack; + +import java.util.List; + +/** + * A class for evaluating arithmetic expressions. + * Operators and operands must be separated by whitespace character. + * Supported operators: +, -, *, /, %, ^ + * Don't use brackets for unary minus. + */ +public class ArithmeticCalculator { + public static void main(String[] args) { + String expression = String.join(" ", args); + PolishNotationCalculator calculator = new PolishNotationCalculator(new Stack<>()); + + try { + List polishNotation = ExpressionParser.toPolishNotation(expression); + System.out.println(calculator.evaluate(polishNotation)); + } catch (ArithmeticException exception) { + System.out.println(exception.getMessage()); + } + } +} diff --git a/Mock/src/main/java/ru/mit/spbau/kazakov/arithmetic/ArithmeticUtility.java b/Mock/src/main/java/ru/mit/spbau/kazakov/arithmetic/ArithmeticUtility.java new file mode 100644 index 0000000..34c3f9e --- /dev/null +++ b/Mock/src/main/java/ru/mit/spbau/kazakov/arithmetic/ArithmeticUtility.java @@ -0,0 +1,158 @@ +package ru.mit.spbau.kazakov.arithmetic; + +import org.jetbrains.annotations.NotNull; +import ru.mit.spbau.kazakov.exception.ArithmeticException; + +import java.util.HashMap; +import java.util.Map; +import java.util.regex.Pattern; + +/** + * A class with arithmetic utilities. + */ +public class ArithmeticUtility { + private static final Map OPERATORS = new HashMap<>(); + + static { + OPERATORS.put("+", Operator.ADDITION); + OPERATORS.put("-", Operator.SUBTRACTION); + OPERATORS.put("*", Operator.MULTIPLICATION); + OPERATORS.put("/", Operator.DIVISION); + OPERATORS.put("%", Operator.MODULO); + OPERATORS.put("^", Operator.EXPONENTIATION); + } + + private enum Operator { + ADDITION(0, true) { + @Override + public double apply(double augend, double addend) { + return augend + addend; + } + }, + SUBTRACTION(0, true) { + @Override + public double apply(double minuend, double subtrahend) { + return minuend - subtrahend; + } + }, + MULTIPLICATION(5, true) { + @Override + public double apply(double multiplier, double multiplicand) { + return multiplier * multiplicand; + } + }, + DIVISION(5, true) { + @Override + public double apply(double dividend, double divisor) throws ArithmeticException { + if (divisor == 0) { + throw new ArithmeticException("Division by zero"); + } + return dividend / divisor; + } + }, + MODULO(5, true) { + @Override + public double apply(double dividend, double divisor) { + return dividend % divisor; + } + }, + EXPONENTIATION(10, false) { + @Override + public double apply(double base, double exponent) { + return Math.pow(base, exponent); + } + }; + + private final int precedence; + private final boolean isLeftAssociative; + + Operator(int precedence, boolean isLeftAssociative) { + this.precedence = precedence; + this.isLeftAssociative = isLeftAssociative; + } + + public abstract double apply(double operand1, double operand2) throws ArithmeticException; + } + + private static final Pattern IS_NUMERIC = Pattern.compile("[+-]?\\d+(\\.\\d+)?"); + + /** + * Checks if specified token is a number. + * + * @param token to check + * @return true if specified token is a number, and false otherwise + */ + public static boolean isNumeric(@NotNull String token) { + return IS_NUMERIC.matcher(token).matches(); + } + + /** + * Checks if specified token is an operator. + * + * @param token to check + * @return true if specified token is an operator, and false otherwise + */ + public static boolean isOperator(@NotNull String token) { + return OPERATORS.containsKey(token); + } + + /** + * Checks if specified operator is left associative. + * + * @param operator to check + * @return true if specified operator is left associative, and false otherwise + */ + public static boolean isLeftAssociative(@NotNull String operator) throws ArithmeticException { + if (!ArithmeticUtility.isOperator(operator)) { + throw new ArithmeticException("Unsupported operator: " + operator); + } + return OPERATORS.get(operator).isLeftAssociative; + } + + /** + * Compares precedences of specified operators. + * + * @param operator1 the first operator + * @param operator2 the second operator + * @return negative number if first operator's precedence is smaller than second operator's precedence, + * zero if precedences are equal, and positive number otherwise. + */ + public static int comparePrecedences(@NotNull String operator1, @NotNull String operator2) throws ArithmeticException { + if (!isOperator(operator1)) { + throw new ArithmeticException("Unsupported operator: " + operator1); + } + if (!isOperator(operator2)) { + throw new ArithmeticException("Unsupported operator: " + operator2); + } + return OPERATORS.get(operator1).precedence - OPERATORS.get(operator2).precedence; + } + + /** + * Determines which of two operators evaluates first. + * + * @param operator1 the first operator + * @param operator2 the second operator + * @return true if the second operator evaluates before the first one, and false otherwise + * @throws ArithmeticException if there is unsupported operator + */ + public static boolean isGoesAfter(@NotNull String operator1, @NotNull String operator2) throws ArithmeticException { + return (isLeftAssociative(operator1) && comparePrecedences(operator1, operator2) <= 0) + || (!isLeftAssociative(operator1) && comparePrecedences(operator1, operator2) < 0); + } + + /** + * Applies specified operator to specified arguments. + * + * @param operator operator for applying + * @param operand1 the first argument + * @param operand2 the second argument + * @return result of application + * @throws ArithmeticException if there is unsupported operator + */ + public static double apply(@NotNull String operator, double operand1, double operand2) throws ArithmeticException { + if (!isOperator(operator)) { + throw new ArithmeticException("Unsupported operator: " + operator); + } + return OPERATORS.get(operator).apply(operand1, operand2); + } +} diff --git a/Mock/src/main/java/ru/mit/spbau/kazakov/arithmetic/ExpressionParser.java b/Mock/src/main/java/ru/mit/spbau/kazakov/arithmetic/ExpressionParser.java new file mode 100644 index 0000000..6d1a1ea --- /dev/null +++ b/Mock/src/main/java/ru/mit/spbau/kazakov/arithmetic/ExpressionParser.java @@ -0,0 +1,60 @@ +package ru.mit.spbau.kazakov.arithmetic; + +import org.jetbrains.annotations.NotNull; +import ru.mit.spbau.kazakov.util.Stack; +import ru.mit.spbau.kazakov.exception.ArithmeticException; + +import java.util.ArrayList; +import java.util.List; + +/** + * A class for parsing mathematical expressions. + * Operators and operands must be separated by whitespace character. + * Supported operators: +, -, *, /, %, ^ + * Don't use brackets for unary minus. + */ +public class ExpressionParser { + /** + * Parses specified expression to Polish notation. + * + * @param expression to parse + * @return resulting stack + * @throws ArithmeticException if input is invalid + */ + public static List toPolishNotation(@NotNull String expression) throws ArithmeticException { + if (expression.trim().length() == 0) { + throw new ArithmeticException("Empty expression"); + } + + ArrayList polishNotation = new ArrayList<>(); + Stack processingStack = new Stack<>(); + + for (String token : expression.trim().split("\\s+")) { + if (ArithmeticUtility.isOperator(token)) { + while (!processingStack.empty() && ArithmeticUtility.isOperator(processingStack.peek()) + && ArithmeticUtility.isGoesAfter(token, processingStack.peek())) { + polishNotation.add(processingStack.pop()); + } + processingStack.push(token); + } else if (token.equals("(")) { + processingStack.push("("); + } else if (token.equals(")")) { + while (!processingStack.empty() && !processingStack.peek().equals("(")) { + polishNotation.add(processingStack.pop()); + } + processingStack.pop(); + } else if (ArithmeticUtility.isNumeric(token)) { + polishNotation.add(token); + } else { + throw new ArithmeticException("Unsupported operator: " + token); + } + } + + while (!processingStack.empty()) { + polishNotation.add(processingStack.pop()); + } + + return polishNotation; + } + +} diff --git a/Mock/src/main/java/ru/mit/spbau/kazakov/arithmetic/PolishNotationCalculator.java b/Mock/src/main/java/ru/mit/spbau/kazakov/arithmetic/PolishNotationCalculator.java new file mode 100644 index 0000000..cec53b0 --- /dev/null +++ b/Mock/src/main/java/ru/mit/spbau/kazakov/arithmetic/PolishNotationCalculator.java @@ -0,0 +1,65 @@ +package ru.mit.spbau.kazakov.arithmetic; + +import com.google.common.collect.Iterables; +import org.jetbrains.annotations.NotNull; +import ru.mit.spbau.kazakov.exception.ArithmeticException; +import ru.mit.spbau.kazakov.exception.EmptyStackException; +import ru.mit.spbau.kazakov.util.Stack; + +import java.util.List; + +/** + * A class for evaluating expressions in Polish notation. + * Supported operators: +, -, *, /, %, ^ + */ +public class PolishNotationCalculator { + /** + * Initializes processing stack. + * + * @param processingStack a stack for evaluating expression + */ + public PolishNotationCalculator(@NotNull Stack processingStack) { + this.processingStack = processingStack; + } + + /** + * Evaluates specified expression. + * + * @param polishNotation an expression in Polish notation + * @return value of the evaluated expression + */ + public double evaluate(@NotNull List polishNotation) throws ArithmeticException { + if (polishNotation.size() == 0) { + throw new ArithmeticException("Empty expression"); + } + + try { + processingStack.push(Double.parseDouble(polishNotation.get(0))); + processingStack.push(Double.parseDouble(polishNotation.get(1))); + + for (String token : Iterables.skip(polishNotation, 2)) { + if (ArithmeticUtility.isOperator(token)) { + double operand2 = processingStack.pop(); + double operand1 = processingStack.pop(); + double operationResult = ArithmeticUtility.apply(token, operand1, operand2); + processingStack.push(operationResult); + } else if (ArithmeticUtility.isNumeric(token)) { + processingStack.push(Double.parseDouble(token)); + } else { + throw new ArithmeticException("Unsupported operator: " + token); + } + } + + if (processingStack.size() != 1) { + throw new ArithmeticException("Invalid expression"); + } + + return processingStack.pop(); + } + catch (EmptyStackException exception) { + throw new ArithmeticException("Invalid expression"); + } + } + + private Stack processingStack; +} diff --git a/Mock/src/main/java/ru/mit/spbau/kazakov/exception/ArithmeticException.java b/Mock/src/main/java/ru/mit/spbau/kazakov/exception/ArithmeticException.java new file mode 100644 index 0000000..4b5b6f2 --- /dev/null +++ b/Mock/src/main/java/ru/mit/spbau/kazakov/exception/ArithmeticException.java @@ -0,0 +1,10 @@ +package ru.mit.spbau.kazakov.exception; + +/** + * Thrown when unsupported arithmetic operation called. + */ +public class ArithmeticException extends Exception { + public ArithmeticException(String message) { + super(message); + } +} diff --git a/Mock/src/main/java/ru/mit/spbau/kazakov/exception/EmptyStackException.java b/Mock/src/main/java/ru/mit/spbau/kazakov/exception/EmptyStackException.java new file mode 100644 index 0000000..8275884 --- /dev/null +++ b/Mock/src/main/java/ru/mit/spbau/kazakov/exception/EmptyStackException.java @@ -0,0 +1,4 @@ +package ru.mit.spbau.kazakov.exception; + +public class EmptyStackException extends RuntimeException { +} diff --git a/Mock/src/main/java/ru/mit/spbau/kazakov/util/Stack.java b/Mock/src/main/java/ru/mit/spbau/kazakov/util/Stack.java new file mode 100644 index 0000000..b12a848 --- /dev/null +++ b/Mock/src/main/java/ru/mit/spbau/kazakov/util/Stack.java @@ -0,0 +1,87 @@ +package ru.mit.spbau.kazakov.util; + +import ru.mit.spbau.kazakov.exception.EmptyStackException; + +/** + * An implementation of stack interface for arithmetic calculator. + * + * @param type of stored data + */ +public class Stack { + /** + * Pushes new element to the top of stack. + * + * @param value to push + * @return pushed value + */ + public T push(T value) { + top = new Node<>(value, top); + size++; + + return value; + } + + /** + * Returns top element. + */ + public T peek() { + if (empty()) { + throw new EmptyStackException(); + } + return top.value; + } + + /** + * Deletes top element from stack. + * + * @return deleted element + */ + public T pop() { + if (empty()) { + throw new EmptyStackException(); + } + + T returnValue = top.value; + top = top.prev; + size--; + + return returnValue; + } + + /** + * Checks if stack is empty. + * + * @return true if stack is empty, and false otherwise. + */ + public boolean empty() { + return top == null; + } + + public int size() { + return size; + } + + private Node top = null; + private int size = 0; + + /** + * A stack entry for storing a value and previous entry. + * + * @param type of stored data + */ + private static class Node { + private T value; + private Node prev; + + /** + * Initializes stored value and reference to previous Node. + * + * @param value to initialize with + * @param prev a previous Node + */ + Node(T value, Node prev) { + this.value = value; + this.prev = prev; + } + } +} diff --git a/Mock/src/test/java/ru/mit/spbau/kazakov/arithmetic/ArithmeticUtilityTest.java b/Mock/src/test/java/ru/mit/spbau/kazakov/arithmetic/ArithmeticUtilityTest.java new file mode 100644 index 0000000..cef8080 --- /dev/null +++ b/Mock/src/test/java/ru/mit/spbau/kazakov/arithmetic/ArithmeticUtilityTest.java @@ -0,0 +1,199 @@ +package ru.mit.spbau.kazakov.arithmetic; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import ru.mit.spbau.kazakov.exception.ArithmeticException; + +import static org.junit.Assert.*; + +public class ArithmeticUtilityTest { + @Rule + public ExpectedException thrown = ExpectedException.none(); + + @Test + public void testIsNumericEmpty() { + assertFalse(ArithmeticUtility.isNumeric("")); + } + + @Test + public void testIsNumericMinus() { + assertFalse(ArithmeticUtility.isNumeric("+")); + } + + @Test + public void testIsNumericPlus() { + assertFalse(ArithmeticUtility.isNumeric("-")); + } + + @Test + public void testIsNumericInt() { + assertTrue(ArithmeticUtility.isNumeric("54576545332")); + } + + @Test + public void testIsNumericDouble() { + assertTrue(ArithmeticUtility.isNumeric("32312.54576545332")); + } + + @Test + public void testIsOperatorPlus() { + assertTrue(ArithmeticUtility.isOperator("+")); + } + + @Test + public void testIsOperatorMinus() { + assertTrue(ArithmeticUtility.isOperator("-")); + } + + @Test + public void testIsOperatorMultiply() { + assertTrue(ArithmeticUtility.isOperator("*")); + } + + @Test + public void testIsOperatorDivide() { + assertTrue(ArithmeticUtility.isOperator("/")); + } + + @Test + public void testIsOperatorModulo() { + assertTrue(ArithmeticUtility.isOperator("%")); + } + + @Test + public void testIsOperatorPow() { + assertTrue(ArithmeticUtility.isOperator("^")); + } + + @Test + public void testIsOperatorIncrement() { + assertFalse(ArithmeticUtility.isOperator("++")); + } + + @Test + public void testIsOperatorXor() { + assertFalse(ArithmeticUtility.isOperator("xor")); + } + + @Test + public void testIsLeftAssociativeThrowsArithmeticException() throws ArithmeticException { + thrown.expect(ArithmeticException.class); + thrown.expectMessage("Unsupported operator: xor"); + ArithmeticUtility.isLeftAssociative("xor"); + } + + @Test + public void testIsLeftAssociativePlus() throws ArithmeticException { + assertTrue(ArithmeticUtility.isLeftAssociative("+")); + } + + @Test + public void testIsLeftAssociativeMinus() throws ArithmeticException { + assertTrue(ArithmeticUtility.isLeftAssociative("-")); + } + + @Test + public void testIsLeftAssociativeMultiply() throws ArithmeticException { + assertTrue(ArithmeticUtility.isLeftAssociative("*")); + } + + @Test + public void testIsLeftAssociativeDivide() throws ArithmeticException { + assertTrue(ArithmeticUtility.isLeftAssociative("/")); + } + + @Test + public void testIsLeftAssociativeModulo() throws ArithmeticException { + assertTrue(ArithmeticUtility.isLeftAssociative("%")); + } + + @Test + public void testIsLeftAssociativePow() throws ArithmeticException { + assertFalse(ArithmeticUtility.isLeftAssociative("^")); + } + + @Test + public void testComparePrecedencesThrowsArithmeticException() throws ArithmeticException { + thrown.expect(ArithmeticException.class); + thrown.expectMessage("Unsupported operator: ++"); + ArithmeticUtility.comparePrecedences("++", "-"); + } + + @Test + public void testComparePrecedencesEqual() throws ArithmeticException { + assertTrue(ArithmeticUtility.comparePrecedences("+", "-") == 0); + } + + @Test + public void testComparePrecedencesLess() throws ArithmeticException { + assertTrue(ArithmeticUtility.comparePrecedences("+", "*") < 0); + } + + @Test + public void testComparePrecedencesGreater() throws ArithmeticException { + assertTrue(ArithmeticUtility.comparePrecedences("^", "%") > 0); + } + + @Test + public void testIsGoesAfterThrowsArithmeticException() throws ArithmeticException { + thrown.expect(ArithmeticException.class); + thrown.expectMessage("Unsupported operator: **"); + ArithmeticUtility.isGoesAfter("+", "**"); + } + + @Test + public void testIsGoesAfterTrue() throws ArithmeticException { + assertTrue(ArithmeticUtility.isGoesAfter("+", "%")); + } + + @Test + public void testIsGoesAfterFalse() throws ArithmeticException { + assertFalse(ArithmeticUtility.isGoesAfter("/", "-")); + } + + @Test + public void testApplyThrowsArithmeticException() throws ArithmeticException { + thrown.expect(ArithmeticException.class); + thrown.expectMessage("Unsupported operator: xor"); + ArithmeticUtility.apply("xor", 5.0, 0.5); + } + + @Test + public void testApplyPlus() throws ArithmeticException { + assertEquals(5.5, ArithmeticUtility.apply("+", 5.0, 0.5), 0.000001); + } + + @Test + public void testApplyMinus() throws ArithmeticException { + assertEquals(103.3, ArithmeticUtility.apply("-", 103.4, 0.1), 0.000001); + } + + @Test + public void testApplyMultiply() throws ArithmeticException { + assertEquals(10.0, ArithmeticUtility.apply("*", 5.0, 2.0), 0.000001); + } + + @Test + public void testApplyDivide() throws ArithmeticException { + assertEquals(5.0, ArithmeticUtility.apply("/", 40.0, 8.0), 0.000001); + } + + @Test + public void testApplyDivideThrowsArithmeticException() throws ArithmeticException { + thrown.expect(ArithmeticException.class); + thrown.expectMessage("Division by zero"); + assertEquals(5.0, ArithmeticUtility.apply("/", 40.0, 0.0), 0.000001); + } + + @Test + public void testApplyModulo() throws ArithmeticException { + assertEquals(1.0, ArithmeticUtility.apply("%", 15.0, 7.0), 0.000001); + } + + @Test + public void testApplyPow() throws ArithmeticException { + assertEquals(64.0, ArithmeticUtility.apply("^", 2.0, 6.0), 0.000001); + } + +} \ No newline at end of file diff --git a/Mock/src/test/java/ru/mit/spbau/kazakov/arithmetic/ExpressionParserTest.java b/Mock/src/test/java/ru/mit/spbau/kazakov/arithmetic/ExpressionParserTest.java new file mode 100644 index 0000000..e0734fb --- /dev/null +++ b/Mock/src/test/java/ru/mit/spbau/kazakov/arithmetic/ExpressionParserTest.java @@ -0,0 +1,36 @@ +package ru.mit.spbau.kazakov.arithmetic; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import ru.mit.spbau.kazakov.exception.ArithmeticException; + +import java.util.Arrays; +import java.util.List; + +import static org.junit.Assert.assertEquals; + +public class ExpressionParserTest { + @Rule + public ExpectedException thrown = ExpectedException.none(); + + @Test + public void testToPolishNotation1() throws ArithmeticException { + List polishNotation = ExpressionParser.toPolishNotation("( 1 + 2 ) * ( 3 / 4 ) ^ ( 5 + 6 )"); + assertEquals(Arrays.asList("1", "2", "+", "3", "4", "/", "5", "6", "+", "^", "*"), polishNotation); + } + + @Test + public void testToPolishNotation2() throws ArithmeticException { + List polishNotation = ExpressionParser.toPolishNotation("( -4 + 6 ) % 3 - ( 5 - 4 ) * ( 11 + 54 )"); + assertEquals(Arrays.asList("-4", "6", "+", "3", "%", "5", "4", "-", "11", "54", "+", "*", "-"), polishNotation); + } + + @Test + public void testToPolishNotationThrowsArithmeticException() throws ArithmeticException { + thrown.expect(ArithmeticException.class); + thrown.expectMessage("Unsupported operator: xor"); + ExpressionParser.toPolishNotation("( 1 + 2 ) * ( 3 / 4 ) ^ ( 5 xor 6 )"); + } + +} \ No newline at end of file diff --git a/Mock/src/test/java/ru/mit/spbau/kazakov/arithmetic/PolishNotationCalculatorTest.java b/Mock/src/test/java/ru/mit/spbau/kazakov/arithmetic/PolishNotationCalculatorTest.java new file mode 100644 index 0000000..29e5c09 --- /dev/null +++ b/Mock/src/test/java/ru/mit/spbau/kazakov/arithmetic/PolishNotationCalculatorTest.java @@ -0,0 +1,100 @@ +package ru.mit.spbau.kazakov.arithmetic; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.mockito.InOrder; +import ru.mit.spbau.kazakov.exception.ArithmeticException; +import ru.mit.spbau.kazakov.util.Stack; + +import java.util.Arrays; + +import static org.junit.Assert.*; +import static org.mockito.Mockito.*; + +public class PolishNotationCalculatorTest { + @Rule + public ExpectedException thrown = ExpectedException.none(); + + @Test + @SuppressWarnings("unchecked") + public void testEvaluateEasy() throws ArithmeticException { + Stack stack = mock(Stack.class); + when(stack.pop()).thenReturn(2.0, -3.0, -1.0); + when(stack.size()).thenReturn(1); + + PolishNotationCalculator calculator = new PolishNotationCalculator(stack); + assertEquals(-1.0, + calculator.evaluate(Arrays.asList("-3", "2", "%")), 0.00001); + + InOrder inOrder = inOrder(stack); + inOrder.verify(stack).push(-3.0); + inOrder.verify(stack).push(2.0); + inOrder.verify(stack, times(2)).pop(); + inOrder.verify(stack).push(-1.0); + inOrder.verify(stack).size(); + inOrder.verify(stack).pop(); + } + + @Test + @SuppressWarnings("unchecked") + public void testEvaluateMedium() throws ArithmeticException { + Stack stack = mock(Stack.class); + when(stack.pop()).thenReturn(2.0, 1.0, 3.0, 4.0, 1.3333333333333333, 3.0, 6.0, 5.0, 11.0, 4.0, 4194304.0); + when(stack.size()).thenReturn(1); + + PolishNotationCalculator calculator = new PolishNotationCalculator(stack); + assertEquals(4194304.0, + calculator.evaluate(Arrays.asList("1", "2", "+", "4", "3", "/", "*", "5", "6", "+", "^")), 0.00001); + + InOrder inOrder = inOrder(stack); + inOrder.verify(stack).push(1.0); + inOrder.verify(stack).push(2.0); + inOrder.verify(stack, times(2)).pop(); + inOrder.verify(stack).push(3.0); + inOrder.verify(stack).push(4.0); + inOrder.verify(stack).push(3.0); + inOrder.verify(stack, times(2)).pop(); + inOrder.verify(stack).push(1.3333333333333333); + inOrder.verify(stack, times(2)).pop(); + inOrder.verify(stack).push(4.0); + inOrder.verify(stack).push(5.0); + inOrder.verify(stack).push(6.0); + inOrder.verify(stack, times(2)).pop(); + inOrder.verify(stack).push(11.0); + inOrder.verify(stack, times(2)).pop(); + inOrder.verify(stack).push(4194304.0); + inOrder.verify(stack).size(); + inOrder.verify(stack).pop(); + } + + @Test + @SuppressWarnings("unchecked") + public void testEvaluateInvalidExpression() throws ArithmeticException { + thrown.expect(ArithmeticException.class); + thrown.expectMessage("Invalid expression"); + + Stack stack = mock(Stack.class); + when(stack.pop()).thenReturn(2.0, 1.0, 1.0, 5.0); + when(stack.size()).thenReturn(2); + + PolishNotationCalculator calculator = new PolishNotationCalculator(stack); + calculator.evaluate(Arrays.asList("1", "2", "%", "5")); + + } + + @Test + @SuppressWarnings("unchecked") + public void testEvaluateUnsupportedOperator() throws ArithmeticException { + thrown.expect(ArithmeticException.class); + thrown.expectMessage("Unsupported operator: div"); + + Stack stack = mock(Stack.class); + when(stack.pop()).thenReturn(2.0, 1.0); + + PolishNotationCalculator calculator = new PolishNotationCalculator(stack); + calculator.evaluate(Arrays.asList("1", "2", "div")); + + } + +} \ No newline at end of file diff --git a/Mock/src/test/java/ru/mit/spbau/kazakov/util/StackTest.java b/Mock/src/test/java/ru/mit/spbau/kazakov/util/StackTest.java new file mode 100644 index 0000000..e8d99b3 --- /dev/null +++ b/Mock/src/test/java/ru/mit/spbau/kazakov/util/StackTest.java @@ -0,0 +1,103 @@ +package ru.mit.spbau.kazakov.util; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import ru.mit.spbau.kazakov.exception.EmptyStackException; + +import static org.junit.Assert.*; + +public class StackTest { + @Rule + public ExpectedException thrown = ExpectedException.none(); + + @Test + public void testConstructor() { + new Stack<>(); + } + + @Test + public void testPushReturnValue() { + Stack stack = new Stack<>(); + assertEquals("abc", stack.push("abc")); + } + + @Test + public void testPeek() { + Stack stack = new Stack<>(); + stack.push("xyz"); + assertEquals("xyz", stack.peek()); + } + + @Test + public void testOneHundredPeek() { + Stack stack = new Stack<>(); + for (int i = 0; i < 100; i++) { + stack.push(Integer.toString(i)); + assertEquals(Integer.toString(i), stack.peek()); + } + } + + @Test + public void testPopThrowsEmptyStackException() throws EmptyStackException { + thrown.expect(EmptyStackException.class); + new Stack<>().pop(); + } + + @Test + public void testPopReturnValue() throws EmptyStackException { + Stack stack = new Stack<>(); + stack.push("."); + assertEquals(".", stack.pop()); + } + + @Test + public void testSizeEmpty() { + assertEquals(0, new Stack<>().size()); + } + + @Test + public void testSizeOnePushed() { + Stack stack = new Stack<>(); + stack.push(33); + assertEquals(1, stack.size()); + } + + @Test + public void testSizeOneHundredPushed() { + Stack stack = new Stack<>(); + for (int i = 0; i < 100; i++) { + stack.push(i); + } + assertEquals(100, stack.size()); + } + + @Test + public void testSizeAfterPop() { + Stack stack = new Stack<>(); + stack.push(21); + stack.push(76); + stack.pop(); + assertEquals(1, stack.size()); + } + + @Test + public void testEmptyEmpty() { + assertTrue(new Stack<>().empty()); + } + + @Test + public void testEmptyAfterPush() { + Stack stack = new Stack<>(); + stack.push("r"); + assertFalse(stack.empty()); + } + + @Test + public void testEmptyAfterPop() { + Stack stack = new Stack<>(); + stack.push("r"); + stack.pop(); + assertTrue(stack.empty()); + } +} \ No newline at end of file