417 lines
No EOL
13 KiB
C++
417 lines
No EOL
13 KiB
C++
#include <vector>
|
|
#include <string>
|
|
#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<Symbol>* _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<unsigned int>* _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<Symbol>* result;
|
|
short unmatchedParanthesis = 0;
|
|
};
|
|
|
|
parseResult* parse(const std::string& _input) {
|
|
parseResult* returnVal = new parseResult;
|
|
returnVal->result = new std::vector<Symbol>();
|
|
|
|
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<Symbol>* _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<unsigned int>& _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<Symbol>* expression in ONP
|
|
*/
|
|
std::vector<Symbol>* toONP(std::vector<Symbol>* _expression) {
|
|
std::vector<Symbol>* expression = new std::vector<Symbol>(); // will not contain brackets
|
|
std::vector<Symbol>* returnVal = new std::vector<Symbol>();
|
|
std::vector<unsigned int> 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<Symbol>& _stack) {
|
|
switch (_stack.back().type) {
|
|
case SymbolType::number: {
|
|
return Stack::take(_stack);
|
|
}
|
|
case SymbolType::add: {
|
|
Stack::take<Symbol>(_stack); //top symbol is add
|
|
Symbol toPut;
|
|
toPut.value = calculateUsingStack(_stack).value + calculateUsingStack(_stack).value;
|
|
Stack::put<Symbol>(_stack, toPut);
|
|
return toPut;
|
|
} break;
|
|
case SymbolType::subract: {
|
|
Stack::take<Symbol>(_stack); //top symbol...
|
|
Symbol toPut;
|
|
toPut.value = calculateUsingStack(_stack).value - calculateUsingStack(_stack).value;
|
|
Stack::put<Symbol>(_stack, toPut);
|
|
return toPut;
|
|
} break;
|
|
case SymbolType::multiply: {
|
|
Stack::take<Symbol>(_stack); //top symbol...
|
|
Symbol toPut;
|
|
toPut.value = calculateUsingStack(_stack).value * calculateUsingStack(_stack).value;
|
|
Stack::put<Symbol>(_stack, toPut);
|
|
return toPut;
|
|
} break;
|
|
case SymbolType::divide: {
|
|
Stack::take<Symbol>(_stack); //top symbol...
|
|
Symbol toPut;
|
|
auto val = calculateUsingStack(_stack).value;
|
|
toPut.value = calculateUsingStack(_stack).value / val;
|
|
Stack::put<Symbol>(_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<char>.
|
|
*
|
|
* @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
|