Initial commit
This commit is contained in:
commit
dabf8a22f6
14 changed files with 1072 additions and 0 deletions
417
src/calculator.cpp
Normal file
417
src/calculator.cpp
Normal file
|
@ -0,0 +1,417 @@
|
|||
#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
|
Reference in a new issue