ShuntingYardTest.java

/*
 * Copyright 2014 Frank Asseg
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package net.objecthunter.exp4j.shuntingyard;

import net.objecthunter.exp4j.operator.Operator;
import net.objecthunter.exp4j.tokenizer.Token;
import org.junit.Test;

import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;

import static net.objecthunter.exp4j.TestUtil.*;

public class ShuntingYardTest {

    @Test
    public void testShuntingYard1() {
        String expression = "2+3";
        Token[] tokens = ShuntingYard.convertToRPN(expression, null, null, null, true);
        assertNumberToken(tokens[0], 2d);
        assertNumberToken(tokens[1], 3d);
        assertOperatorToken(tokens[2], "+", 2, Operator.PRECEDENCE_ADDITION);
    }

    @Test
    public void testShuntingYard2() {
        String expression = "3*x";
        Token[] tokens = ShuntingYard.convertToRPN(expression, null, null, new HashSet<>(Collections.singletonList("x")), true);
        assertNumberToken(tokens[0], 3d);
        assertVariableToken(tokens[1], "x");
        assertOperatorToken(tokens[2], "*", 2, Operator.PRECEDENCE_MULTIPLICATION);
    }

    @Test
    public void testShuntingYard3() {
        String expression = "-3";
        Token[] tokens = ShuntingYard.convertToRPN(expression, null, null, null, true);
        assertNumberToken(tokens[0], 3d);
        assertOperatorToken(tokens[1], "-", 1, Operator.PRECEDENCE_UNARY_MINUS);
    }

    @Test
    public void testShuntingYard4() {
        String expression = "-2^2";
        Token[] tokens = ShuntingYard.convertToRPN(expression, null, null, null, true);
        assertNumberToken(tokens[0], 2d);
        assertNumberToken(tokens[1], 2d);
        assertOperatorToken(tokens[2], "^", 2, Operator.PRECEDENCE_POWER);
        assertOperatorToken(tokens[3], "-", 1, Operator.PRECEDENCE_UNARY_MINUS);
    }

    @Test
    public void testShuntingYard5() {
        String expression = "2^-2";
        Token[] tokens = ShuntingYard.convertToRPN(expression, null, null, null, true);
        assertNumberToken(tokens[0], 2d);
        assertNumberToken(tokens[1], 2d);
        assertOperatorToken(tokens[2], "-", 1, Operator.PRECEDENCE_UNARY_MINUS);
        assertOperatorToken(tokens[3], "^", 2, Operator.PRECEDENCE_POWER);
    }

    @Test
    public void testShuntingYard6() {
        String expression = "2^---+2";
        Token[] tokens = ShuntingYard.convertToRPN(expression, null, null, null, true);
        assertNumberToken(tokens[0], 2d);
        assertNumberToken(tokens[1], 2d);
        assertOperatorToken(tokens[2], "+", 1, Operator.PRECEDENCE_UNARY_PLUS);
        assertOperatorToken(tokens[3], "-", 1, Operator.PRECEDENCE_UNARY_MINUS);
        assertOperatorToken(tokens[4], "-", 1, Operator.PRECEDENCE_UNARY_MINUS);
        assertOperatorToken(tokens[5], "-", 1, Operator.PRECEDENCE_UNARY_MINUS);
        assertOperatorToken(tokens[6], "^", 2, Operator.PRECEDENCE_POWER);
    }

    @Test
    public void testShuntingYard7() {
        String expression = "2^-2!";
        Operator factorial = new Operator("!", 1, true, Operator.PRECEDENCE_POWER + 1) {

            @Override
            public double apply(double... args) {
                final int arg = (int) args[0];
                if ((double) arg != args[0]) {
                    throw new IllegalArgumentException("Operand for factorial has to be an integer");
                }
                if (arg < 0) {
                    throw new IllegalArgumentException("The operand of the factorial can not be less than zero");
                }
                double result = 1;
                for (int i = 1; i <= arg; i++) {
                    result *= i;
                }
                return result;
            }
        };
        Map<String, Operator> userOperators = new HashMap<>();
        userOperators.put("!", factorial);
        Token[] tokens = ShuntingYard.convertToRPN(expression, null, userOperators, null, true);
        assertNumberToken(tokens[0], 2d);
        assertNumberToken(tokens[1], 2d);
        assertOperatorToken(tokens[2], "!", 1, Operator.PRECEDENCE_POWER + 1);
        assertOperatorToken(tokens[3], "-", 1, Operator.PRECEDENCE_UNARY_MINUS);
        assertOperatorToken(tokens[4], "^", 2, Operator.PRECEDENCE_POWER);
    }

    @Test
    public void testShuntingYard8() {
        String expression = "-3^2";
        Token[] tokens = ShuntingYard.convertToRPN(expression, null, null, null, true);
        assertNumberToken(tokens[0], 3d);
        assertNumberToken(tokens[1], 2d);
        assertOperatorToken(tokens[2], "^", 2, Operator.PRECEDENCE_POWER);
        assertOperatorToken(tokens[3], "-", 1, Operator.PRECEDENCE_UNARY_MINUS);
    }

    @Test
    public void testShuntingYard9() {
        Operator reciprocal = new Operator("$", 1, true, Operator.PRECEDENCE_DIVISION) {
            @Override
            public double apply(final double... args) {
                if (args[0] == 0d) {
                    throw new ArithmeticException("Division by zero!");
                }
                return 1d / args[0];
            }
        };
        Map<String, Operator> userOperators = new HashMap<>();
        userOperators.put("$", reciprocal);
        Token[] tokens = ShuntingYard.convertToRPN("1$", null, userOperators, null, true);
        assertNumberToken(tokens[0], 1d);
        assertOperatorToken(tokens[1], "$", 1, Operator.PRECEDENCE_DIVISION);
    }

}