#include #include #include "stack.cpp" namespace Calculator { enum SymbolType { number, add, subract, multiply, divide, left_bracket, right_bracket, }; /** * @brief Represents one symbol. */ struct Symbol { SymbolType type = SymbolType::number; double value = 0; }; void dbgPrint(const std::vector* _input) { std::cout << "\033[31m"; for (auto it = _input->begin(); it != _input->end(); it++) { auto type = it->type; std::cout << "["; if (type == SymbolType::add) std::cout << "+"; else if (type == SymbolType::subract) std::cout << "-"; else if (type == SymbolType::divide) std::cout << "/"; else if (type == SymbolType::multiply) std::cout << "*"; else if (type == SymbolType::number) std::cout << it->value; else if (type == SymbolType::left_bracket) std::cout << "("; else if (type == SymbolType::right_bracket) std::cout << ")"; std::cout << "]"; }; std::cout << "\033[0m"; } void dbgPrint(const std::vector* _input) { std::cout << "\033[31m"; for (auto it = _input->begin(); it != _input->end(); it++) { std::cout << "[" << *it << "]"; } std::cout << "\033[31m"; } /** * @brief the result of parse function. */ struct parseResult { std::vector* result; short unmatchedParanthesis = 0; }; parseResult* parse(const std::string& _input) { parseResult* returnVal = new parseResult; returnVal->result = new std::vector(); for (unsigned int i = 0; i < _input.size(); i++) { switch (_input[i]) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case '.': { int length = 0; auto j = i; std::string textifiedValue = ""; // I don't know how to avoid duplication :< while ( // while character is one of 0123456789. and it's not the end of _input ( (_input[j] >= 0x30 && _input[j] <= 0x39) || // 0123456789 _input[j] == '.' // . ) && j < _input.size() ) { textifiedValue += _input[j]; j++; } Symbol symbol; symbol.value = std::atof(textifiedValue.c_str()); returnVal->result->push_back(symbol); i = j - 1; } break; case '+': { Symbol symbol; symbol.type = SymbolType::add; returnVal->result->push_back(symbol); } break; case '-': { Symbol symbol; symbol.type = SymbolType::subract; returnVal->result->push_back(symbol); } break; case '*': { Symbol symbol; symbol.type = SymbolType::multiply; returnVal->result->push_back(symbol); } break; case '/': { Symbol symbol; symbol.type = SymbolType::divide; returnVal->result->push_back(symbol); } break; case '(': { Symbol symbol; symbol.type = SymbolType::left_bracket; returnVal->result->push_back(symbol); returnVal->unmatchedParanthesis++; } break; case ')': { Symbol symbol; symbol.type = SymbolType::right_bracket; returnVal->result->push_back(symbol); returnVal->unmatchedParanthesis--; } break; default: // it should be impossible to get there static_assert(true); } } return returnVal; } /** * @brief corrects negative signs in input * * @param _input what to correct */ void correctNegativeSign(parseResult* _input) { auto parsed = _input->result; for (auto it = parsed->begin(); it != parsed->end(); it++) { auto jt = it + 1; //next element if (it->type == SymbolType::subract && jt != parsed->end() && jt->type == SymbolType::number) { it = parsed->erase(it); it->value = it->value * (-1); continue; } } parsed->shrink_to_fit(); } /** * @brief validates parsation * * @param _input * @return true if valid * @return false if invalid */ bool validateParsation(const std::vector* _input) { return true; if (_input->size() == 0) return false; for (auto it = _input->begin(); true /* Break argument 2 lines down */; it++) { auto jt = it + 1; auto kt = jt + 1; if (jt == _input->end()) { if (it->type == SymbolType::add) return false; if (it->type == SymbolType::subract) return false; if (it->type == SymbolType::multiply) return false; if (it->type == SymbolType::divide) return false; break; } if (kt == _input->end()) { if (jt->type == SymbolType::left_bracket) { if (it->type == SymbolType::add) return false; if (it->type == SymbolType::subract) return false; if (it->type == SymbolType::multiply) return false; if (it->type == SymbolType::divide) return false; } } switch (it->type) { case SymbolType::number: if (jt->type == SymbolType::number) return false; if (jt->type == SymbolType::left_bracket) return false; break; case SymbolType::left_bracket: // sign cannot be after left bracket, only number can be case SymbolType::add: case SymbolType::subract: case SymbolType::multiply: case SymbolType::divide: if (jt->type == SymbolType::add) return false; if (jt->type == SymbolType::subract) return false; if (jt->type == SymbolType::multiply) return false; if (jt->type == SymbolType::divide) return false; break; case SymbolType::right_bracket: if (jt->type == SymbolType::number) return false; break; } } return true; } /** * @brief finds maximum in _vector. * * @param _vector in which vector find max. * @return unsigned int the position of maximum value. */ unsigned int findMaxPosition(const std::vector& _vector) { int maxPosition = 0; int maxValue = 0; for (int i = 0; i < _vector.size(); i++) { if (_vector[i] > maxValue) { maxPosition = i; maxValue = _vector[i]; } } return maxPosition; } /** * @brief Converts _expression to ONP * * @param _expression which expression to convert * @return std::vector* expression in ONP */ std::vector* toONP(std::vector* _expression) { std::vector* expression = new std::vector(); // will not contain brackets std::vector* returnVal = new std::vector(); std::vector priorities; unsigned int bracketPriority = 0; unsigned int size = _expression->size(); expression->reserve(size); returnVal->reserve(size); priorities.reserve(size); // 1. Copy _expression to expression removing paranthesis // NOTE: paranthesis should be matched before. for (unsigned int i = 0; i < _expression->size(); i++) { switch (_expression->at(i).type) { case SymbolType::number: expression->push_back(_expression->at(i)); priorities.push_back((1<<0) + bracketPriority * (1<<3)); break; case SymbolType::add: case SymbolType::subract: expression->push_back(_expression->at(i)); priorities.push_back((1<<1) + bracketPriority * (1<<3)); break; case SymbolType::multiply: case SymbolType::divide: expression->push_back(_expression->at(i)); priorities.push_back((1<<2) + bracketPriority * (1<<3)); break; case SymbolType::left_bracket: bracketPriority++; break; case SymbolType::right_bracket: bracketPriority--; break; } } size = expression->size(); // 2. Actually convert to ONP if (size > 0) while (true) { unsigned int maxOnPos = findMaxPosition(priorities); if (priorities[maxOnPos] == 0) break; if (maxOnPos >= 1 && maxOnPos - 1 < size && priorities[maxOnPos-1] != 0) { // left returnVal->push_back(expression->at(maxOnPos-1)); priorities[maxOnPos-1] = 0; } if (maxOnPos >= 0 && maxOnPos + 1 < size && priorities[maxOnPos+1] != 0) { // right returnVal->push_back(expression->at(maxOnPos+1)); priorities[maxOnPos+1] = 0; } if (maxOnPos >= 0 && maxOnPos < size && priorities[maxOnPos] != 0) { // center (maximum element) returnVal->push_back(expression->at(maxOnPos)); priorities[maxOnPos] = 0; } } delete expression; return returnVal; } Symbol calculateUsingStack(std::vector& _stack) { switch (_stack.back().type) { case SymbolType::number: { return Stack::take(_stack); } case SymbolType::add: { Stack::take(_stack); //top symbol is add Symbol toPut; toPut.value = calculateUsingStack(_stack).value + calculateUsingStack(_stack).value; Stack::put(_stack, toPut); return toPut; } break; case SymbolType::subract: { Stack::take(_stack); //top symbol... Symbol toPut; toPut.value = calculateUsingStack(_stack).value - calculateUsingStack(_stack).value; Stack::put(_stack, toPut); return toPut; } break; case SymbolType::multiply: { Stack::take(_stack); //top symbol... Symbol toPut; toPut.value = calculateUsingStack(_stack).value * calculateUsingStack(_stack).value; Stack::put(_stack, toPut); return toPut; } break; case SymbolType::divide: { Stack::take(_stack); //top symbol... Symbol toPut; auto val = calculateUsingStack(_stack).value; toPut.value = calculateUsingStack(_stack).value / val; Stack::put(_stack, toPut); return toPut; } break; default: throw "Impossible"; } } /** * @brief the return value of parseAndCalculate */ struct parseAndCalculateResult { int unmatchedParanthesis = 0; bool valid = true; double value = 0; }; /** * @brief Parses and calculates the value of math expression expressed in std::vector. * * @param _input mathematical expression. * @return parseAndCalculateResult the result of solving it. */ parseAndCalculateResult parseAndCaluclate(const std::string& _input, bool debug = false) { parseAndCalculateResult returnVal; // 0. return if _input is empty if (_input.size() == 0) return returnVal; // 1. Parse input parseResult* parsed = parse(_input); // 2. if unmatchedBrackes > 0, add unmatched brackets returnVal.unmatchedParanthesis = parsed->unmatchedParanthesis; // Let's not forget that we still return it :^) while (parsed->unmatchedParanthesis > 0) { Symbol sym; sym.type = SymbolType::right_bracket; parsed->unmatchedParanthesis--; parsed->result->push_back(sym); } // 3. if unmatchedBrackets < 0, throw something or... abort // display amount of missing opening brackets in red at the end, // if it's more than 3, display number, not individual ones // IMPLEMENT DISPLAYING IT SOMEWHERE ELSE!!! if (parsed->unmatchedParanthesis < 0) { returnVal.unmatchedParanthesis = parsed->unmatchedParanthesis; return returnVal; }; // 4. correct minus sign correctNegativeSign(parsed); // 5. Validate parsation. If invalid, set appripraite flag and return. returnVal.valid = validateParsation(parsed->result); if (!returnVal.valid) { delete parsed; return returnVal; } // 6. Convet to Reversed Polish Adnotation (ONP) auto* onped = toONP(parsed->result); delete parsed; // 7. if onped vector is length of 0, invalidate and return. if (onped->size() == 0) { delete onped; return returnVal; } // 8. do a stacky wacky recursive onp magic to calculate the result try { returnVal.value = calculateUsingStack(*onped).value; } catch (char const* e) { //everything is fine returnVal.value = onped->end()->value; } delete onped; return returnVal; } }; // namespace Calculator