From c5431e4d321dbf0152e6826841c1ef5c1291ba5b Mon Sep 17 00:00:00 2001 From: Syndamia Date: Tue, 14 Jun 2022 15:38:50 +0300 Subject: Added files from project 2 (mirror) --- .../Project 2/src/Models/CellFormula.cpp | 190 +++++++++++++++++++++ 1 file changed, 190 insertions(+) create mode 100644 Year 1/Object-oriented Programming/Project 2/src/Models/CellFormula.cpp (limited to 'Year 1/Object-oriented Programming/Project 2/src/Models/CellFormula.cpp') diff --git a/Year 1/Object-oriented Programming/Project 2/src/Models/CellFormula.cpp b/Year 1/Object-oriented Programming/Project 2/src/Models/CellFormula.cpp new file mode 100644 index 0000000..1cdf06a --- /dev/null +++ b/Year 1/Object-oriented Programming/Project 2/src/Models/CellFormula.cpp @@ -0,0 +1,190 @@ +#include "CellFormula.h" +#include "CellDouble.h" +#include +#include +#include + +const Cell* CellFormula::ptrByInd(Pair loc) const { + if (loc.left < 0) { + if (loc.right < 0 || localCells.get_count() - 1 < loc.right) return nullptr; + return &localCells[loc.right]; + } + + if (loc.right < 0 + || (*tableCells).get_count() - 1 < loc.left || (*tableCells)[loc.left].get_count() - 1 < loc.right) + return nullptr; + return (*tableCells)[loc.left][loc.right]; +} + +double pow(double x, int y) { + if (y <= 0) return 1; + + double res = x; + for (unsigned i = 1; i < y; i++) + res *= x; + return res; +} + +double CellFormula::calculate(unsigned index) const { + const Cell* currCell = ptrByInd(formula[index].left); + double numVal = (currCell == nullptr) ? 0 : currCell->getNumeralValue(); + + // Handle prioritised operation + while (formula[index].right < 0) { + switch (-formula[index++].right) { // Prioritized operations are stored as negative characters + case '+' : numVal += ptrByInd(formula[index].left)->getNumeralValue(); break; + case '*' : numVal *= ptrByInd(formula[index].left)->getNumeralValue(); break; + case '/' : if (ptrByInd(formula[index].left)->getNumeralValue() == 0) throw std::logic_error("Error: Divion by zero!"); + numVal /= ptrByInd(formula[index].left)->getNumeralValue(); break; + case '^' : numVal = pow(numVal, ptrByInd(formula[index].left)->getNumeralValue()); break; + } + } + + double temp; + switch(formula[index].right) { + case '+' : return numVal + calculate(++index); + case '*' : return numVal * calculate(++index); + case '/' : temp = calculate(++index); + if (temp == 0) throw std::logic_error("Error: Division by zero!"); + return numVal / temp; + case '^' : return pow(numVal, calculate(++index)); + case '\0' : + default : return numVal; + } +} + +CellFormula::CellFormula(const char* str, const List>* tableCells) : localCells(), formula(), rawFormula() { + this->tableCells = tableCells; + CellFormula::parseAndSetValue(str); +} + +#include + +/*! + * Value is recalculated every time this function is called (in case a referenced cell is changed). + * + * \return 0 \if Calculation failed + * \return Calculated value \otherwise + */ +double CellFormula::getNumeralValue() const { + try { + return calculate(); + } + catch (std::logic_error e) { + return 0; + } +} + +/*! + * Value is recalculated every time this function is called (in case a referenced cell is changed). + * + * \return "Error" \if Calculation failed + * \return Calculated value as a String \otherwise + */ +String CellFormula::getValueForPrint() const { + try { + return String() += calculate(); + } + catch (std::logic_error e) { + return "Error"; + } +} + +// Rough implementation of the grammar +// +// S -> WDWOS | WD.DWOS | WRDCDWOS | W +// O -> + | - | * | / | ^ +// D -> D0 | D1 | ... | D9 | 0 | 1 | ... | 9 +// W -> W | epsilon +// +// over the string + +void W(const char*& str) { + while (*str == ' ') str++; +} + +void D(const char*& str, List& buffer) { + while (*str >= '0' && *str <= '9') + buffer.add(*(str++)); +} + +void O(const char*& str, char& buffer) { + if (*str == '+' || *str == '-' || *str == '*' || *str == '/' || *str == '^' || *str == '\0') + buffer = *(str++); + else buffer = -2; +} + +/*! + * Each math operation is represented with a Pair that holds indecies of cell reference + * and a character for the operation. + * + * Negatve value for the operation character means it's with priority and the result get's calculated + * immediately (rather than recursively, taking into account other values). + * + * \exception std::logic_error("Error: Invalid column character!") Thrown when there is something between Rx and C in cell references + * \exception std::logic_error("Error: Invalid value!") Thrown when met value isn't a cell reference or number + * \exception throw std::logic_error("Error: Could not find operand!") Thrown when there isn't an operator character between two values + */ +void CellFormula::parseAndSetValue(const char* str) { + rawFormula = String(str); + + formula.clear(); + localCells.clear(); + Pair loc; + char currOp = -2; + + str++; // Removes = from the beginning + while (*str != '\0') { + List buffer; + W(str); + if (*str == 'R') { + D(++str, buffer); + loc.left = atoi(buffer.raw_data()) - 1; + if (*str != 'C') throw std::logic_error("Error: Invalid column character!"); + str++; + buffer.clear(); + D(str, buffer); + loc.right = atoi(buffer.raw_data()) - 1; + } + else if (*str >= '0' && *str <= '9') { + D(str, buffer); + if (*str == '.') { + buffer.add(*str); + D(++str, buffer); + localCells.add(CellDouble(atof(buffer.raw_data()))); + } + else + localCells.add(CellDouble(atoi(buffer.raw_data()))); + + loc.left = -1; + loc.right = (int)(localCells.get_count() - 1); + } + else + throw std::logic_error("Error: Invalid value!"); + + W(str); + O(str, currOp); + if (currOp == -2) throw std::logic_error("Error: Could not find operand!"); + + // Operator priority magic + if (currOp == '^') currOp *= -1; + else if ((currOp == '+' || currOp == '-') && formula.get_count() != 0) { + for (unsigned i = formula.get_count() - 1; i < formula.get_count() && (formula[i].right == '*' || formula[i].right == '/'); i--) + if (formula[i].right > 0) formula[i].right *= -1; + } + + if (currOp == '-') { // a - b turns into a + -1 * b + formula.add({loc, '+'}); + localCells.add(CellDouble(-1.0)); + loc.left = -1; + loc.right = (int)(localCells.get_count() - 1); + currOp = '*' * -1; + } + + formula.add(Pair, char>(loc, currOp)); + } +} + +void CellFormula::writeToFile(std::ofstream& file) { + file << '"' << rawFormula << '"'; +} -- cgit v1.3.1-23-gd82d