This repository has been archived on 2022-05-26. You can view files and clone it, but you cannot make any changes to it's state, such as pushing and creating new issues, pull requests or comments.
calculator/src/calculator.cpp
2022-05-15 21:47:33 +02:00

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