#include <iostream>
#include <vector>
#include <list>
#include <math.h>

class Token
{
public:
    Token(const char);
    Token(std::string num);
    enum Type { RBracket, LBracket, Num, Space, Dot, Add, Sub, Div, Mul, Pow, Undefined };
    enum Priority { Low, Average, High, Braces };
    std::string symbol();
    Type type();
    Priority priority();

private:
    std::string _symbol;
    Type _type;
    Priority _priority;

};

// TODO: add Undefined
Token::Token(char ch)
{
    _symbol = ch;

    if (isdigit(ch))
        _type = Num;

    switch (ch) {
    case ' ':
        _type = Space;
        break;
    case '.':
        _type = Dot;
        break;
    case '+':
        _type = Add;
        _priority = Low;
        break;
    case '-':
        _type = Sub;
        _priority = Low;
        break;
    case '/':
        _type = Div;
        _priority = Average;
        break;
    case '*':
        _type = Mul;
        _priority = Average;
        break;
    case '^':
        _type = Pow;
        _priority = High;
        break;
    case '(':
        _type = LBracket;
        _priority = Braces;
        break;
    case ')':
        _type = RBracket;
        _priority = Braces;
        break;
    }
}

// It is always number
Token::Token(std::string num)
{
    _type = Num;
    _symbol = num;
}

std::string Token::symbol()
{
    return _symbol;
}

Token::Type Token::type()
{
    return _type;
}

Token::Priority Token::priority()
{
    return _priority;
}

std::vector<Token> toRPN(std::string str)
{
    size_t len = str.length();
    std::string number; // glue number
    bool glueNumber = false;  // we are alrady gluing?
    int prevType;
    std::vector<Token> tokens;
    std::vector<Token> result;

    for (unsigned i = 0; i < len; ++i) {
        Token tok(str[i]);
        if (tok.type() == Token::Undefined)
            fprintf(stderr, "Undefined token <%s>\n", tok.symbol().c_str());
        if (tok.type() == Token::Space) {
            if (glueNumber) {
                result.push_back(Token(number));
                glueNumber = false;
                number.clear();
            }
            continue;
        }

        if (tok.type() == Token::Sub) {
            if (prevType != Token::Num) {
                if (!glueNumber) {
                    number += tok.symbol();
                } else {
                    fprintf(stderr, "Undefined token <%s>\n", tok.symbol().c_str());
                }
                continue;
            }
        }

        // For determine if minus (-) is '-3' or '5 - 3'
        prevType = tok.type();

        // It is dot without number .4 + 3
        if (tok.type() == Token::Dot) {
            if (glueNumber) {
                number += tok.symbol();
            } else {
                fprintf(stderr, "Undefined token <%s>\n", tok.symbol().c_str());
            }
            continue;
        }

        if (tok.type() == Token::Num) {
            number += tok.symbol();
            glueNumber = true;
            if (i == len - 1) {
                result.push_back(Token(number));
            }
        } else {
            if (glueNumber) {
                result.push_back(Token(number));
                glueNumber = false;
                number = "";
            }

            while(tokens.size() > 0 && tok.type() != Token::RBracket) {
                Token prev = tokens.back();
                if (prev.priority() >= tok.priority() && prev.priority() != Token::Braces) {
                    result.push_back(prev);
                    tokens.pop_back();
                } else {
                    break;
                }
            }

            if (tok.type() != Token::RBracket)
                tokens.push_back(tok);
        }

        if (tok.type() == Token::RBracket) {
            Token prev = tokens.back();
            while (prev.type() != Token::LBracket) {
                result.push_back(prev);
                tokens.pop_back();
                prev = tokens.back();
            }
            tokens.pop_back(); // delete LBracket
        }
    }

    // Reverse iteration. Add remaining tokens to result
    for (std::vector<Token>::reverse_iterator rit = tokens.rbegin(); rit!= tokens.rend(); ++rit) {
        result.push_back(*rit);
    }

    return result;
}

// If exspression will ended with error we'll show it!
std::string calculateError;
double calculate(std::vector<Token> tokens)
{
    std::vector<double> stack;
    double val1;
    double val2;
    double result;

    // If it was any error we have to clear it!
    calculateError.clear();

    for (Token &tok : tokens) {

        if (tok.type() == Token::Num) {
            stack.push_back(atof(tok.symbol().c_str()));
            continue;
        }

        // Minimum for operation
        if (stack.size() > 1) {

            // Get last two numbers from stack
            val2 = stack.back();
            stack.pop_back();
            val1 = stack.back();
            stack.pop_back();

            switch (tok.type()) {
            case Token::Add:
                stack.push_back(val1 + val2);
                break;
            case Token::Sub:
                stack.push_back(val1 - val2);
                break;
            case Token::Mul:
                stack.push_back(val1 * val2);
                break;
            case Token::Div:
                if (val2 == 0) {
                    calculateError.append("Divided by zero");
                }
                stack.push_back(val1 / val2);
                break;
            case Token::Pow:
                stack.push_back(pow(val1, val2));
                break;
            default:
                break;
            }
        } else {
            calculateError.append("Does not correct expression");
            break;
        }
    }

    if (stack.size() == 1) {
        double temp = stack.back();
        result = temp;
    } else {
        calculateError.append("Does not correct expression");
    }

    return result;
}

void test_calculate()
{
    std::list<std::string> lst;
    lst.push_back("2 + 3");
    lst.push_back("4 - 3");
    lst.push_back("2 + (-3)");
    lst.push_back("4 * 5");
    lst.push_back("6/4");
    lst.push_back("1.2 + 1/2");
    lst.push_back("1/(-3)");
    lst.push_back("0.5 + 0.2");
    lst.push_back("3 ^ 2 ^ 2");
    lst.push_back("17654/342");
    lst.push_back("2/3 ^ 2");
    lst.push_back("(2/3) ^ 2");
    lst.push_back("(2 + 3) / (2 - 2) ");
    lst.push_back("2 + 345 + + + + 6 ");

    std::vector<Token> tokens;
    double result;
    std::string resultOfRPN;
    for (std::string &item : lst) {
        tokens = toRPN(item);
        for (Token &tok : tokens) {
            resultOfRPN.append(tok.symbol() + " ");
        }
        result = calculate(tokens);

        if (calculateError.size() == 0)
            printf("%-18s %.6g\n", resultOfRPN.c_str(), result);
        else
            printf("%-18s %s\n", resultOfRPN.c_str(), calculateError.c_str());

        // reset
        resultOfRPN.clear();
    }
}

int main()
{
    test_calculate();

    return 0;
}
