diff --git a/QuicMaf/QuicMaf.vcxproj b/QuicMaf/QuicMaf.vcxproj index 9e5de4f..ea07cf6 100644 --- a/QuicMaf/QuicMaf.vcxproj +++ b/QuicMaf/QuicMaf.vcxproj @@ -131,6 +131,7 @@ + diff --git a/QuicMaf/QuicMaf.vcxproj.filters b/QuicMaf/QuicMaf.vcxproj.filters index 1241da6..ccaf52c 100644 --- a/QuicMaf/QuicMaf.vcxproj.filters +++ b/QuicMaf/QuicMaf.vcxproj.filters @@ -74,5 +74,8 @@ Header Files + + Header Files + \ No newline at end of file diff --git a/QuicMaf/app.cpp b/QuicMaf/app.cpp index 8e81254..ac1a3ca 100644 --- a/QuicMaf/app.cpp +++ b/QuicMaf/app.cpp @@ -70,13 +70,15 @@ int main() { int main() { lexertk::generator lexer; - lexer.process("2(x + 2x) + 2x"); + lexer.process("2x/(4x+2)"); auto result = tokenize(lexer); QMReducer reducer; reducer.setPool(result); - auto res = reducer.Sub(result[0], result[2]); + auto frac = static_cast(result[0]); + + auto res = reducer.Div(frac->mNomin[0], frac->mDomin[0]); return 0; } diff --git a/QuicMaf/maths/defines.h b/QuicMaf/maths/defines.h index 7b1de31..9382791 100644 --- a/QuicMaf/maths/defines.h +++ b/QuicMaf/maths/defines.h @@ -29,6 +29,7 @@ enum TermTypes { Op, Brack, Equ, + Frac, }; // for string delimiter diff --git a/QuicMaf/maths/solver/term_rewriter/QMReducer.cpp b/QuicMaf/maths/solver/term_rewriter/QMReducer.cpp index 9adb506..c7f736b 100644 --- a/QuicMaf/maths/solver/term_rewriter/QMReducer.cpp +++ b/QuicMaf/maths/solver/term_rewriter/QMReducer.cpp @@ -63,7 +63,7 @@ Bracket * QMReducer::convertToBracket(Term * t1) { if (t1->mType != TermTypes::Brack) return false; return static_cast(t1); } -Variable * QMReducer::converToVariable(Term * t1) { +Variable * QMReducer::convertToVariable(Term * t1) { if (t1->mType != TermTypes::Var) return false; return static_cast(t1); } @@ -190,8 +190,8 @@ vector QMReducer::Add(Term * t1, Term * t2, Identifier_t order) { return { Const }; } else if (t1->mType == TermTypes::Var && t2->mType == TermTypes::Var) { - auto t1_var = converToVariable(t1); - auto t2_var = converToVariable(t2); + auto t1_var = convertToVariable(t1); + auto t2_var = convertToVariable(t2); Variable* var = nullptr; var = new Variable(); @@ -265,8 +265,8 @@ vector QMReducer::Sub(Term * t1, Term * t2, Identifier_t order) { return { Const }; } else if (t1->mType == TermTypes::Var && t2->mType == TermTypes::Var) { - auto t1_var = converToVariable(t1); - auto t2_var = converToVariable(t2); + auto t1_var = convertToVariable(t1); + auto t2_var = convertToVariable(t2); Variable* var = nullptr; var = new Variable(); @@ -346,8 +346,8 @@ vector QMReducer::Mul(Term * t1, Term * t2, Identifier_t order) { else { // no need to check solvability if (t1->mType == TermTypes::Var && t2->mType == TermTypes::Var) { - auto t1_var = converToVariable(t1); - auto t2_var = converToVariable(t2); + auto t1_var = convertToVariable(t1); + auto t2_var = convertToVariable(t2); Variable* var = nullptr; var = new Variable(); @@ -357,7 +357,7 @@ vector QMReducer::Mul(Term * t1, Term * t2, Identifier_t order) { return { var }; } else if (t1->mType == TermTypes::Var && t2->mType == TermTypes::Const) { - auto t1_var = converToVariable(t1); + auto t1_var = convertToVariable(t1); auto t2_const = convertToConstant(ReducePower(t2)[0]); Variable* var = nullptr; @@ -369,7 +369,7 @@ vector QMReducer::Mul(Term * t1, Term * t2, Identifier_t order) { } else if (t1->mType == TermTypes::Const && t2->mType == TermTypes::Var) { auto t1_const = convertToConstant(t1); - auto t2_var = converToVariable(ReducePower(t2)[0]); + auto t2_var = convertToVariable(ReducePower(t2)[0]); Variable* var = nullptr; var = new Variable(); @@ -391,3 +391,232 @@ vector QMReducer::Mul(Term * t1, Term * t2, Identifier_t order) { } return res; } + +bool QMReducer::IsDivSolvable(Term * t1, Term * t2) { + if (t1->mType == TermTypes::Var && t2->mType == TermTypes::Var) return true; + if (t1->mType == TermTypes::Const && t2->mType == TermTypes::Const) return true; + + return false; +} + +bool QMReducer::IsDivSpecialCase(Term * t1, Term * t2) { + if (t1->mType == TermTypes::Brack && + t2->mType != TermTypes::Brack) return true; + + if (t1->mType != TermTypes::Brack && + t2->mType == TermTypes::Brack) return true; + + if (t1->mType == TermTypes::Brack && + t2->mType == TermTypes::Brack) return true; + + if (t1->mType == TermTypes::Const && + t2->mType == TermTypes::Var) return true; + + return false; +} + +vector QMReducer::gcdofTerms(Term * t1, Term * t2) { + + if (t1->mType == TermTypes::Const && t2->mType == TermTypes::Const) { + auto t1_const = convertToConstant(ReducePower(t1)[0]); + auto t2_const = convertToConstant(ReducePower(t2)[0]); + + Constant* Const = new Constant(); + Const->mValue = gcf(t1->mValue, t2->mValue); + return { Const }; + } + else if (t1->mType == TermTypes::Var && t2->mType == TermTypes::Var) { + auto t1_var = convertToVariable(t1); + auto t2_var = convertToVariable(t2); + + Variable* var = new Variable(); + var->mValue = gcf(t1_var->mValue, t2_var->mValue); + var->mVariable = t1_var->mVariable; + var->mPower = (t1_var->mPower > t2_var->mPower) ? t2_var->mPower : t1_var->mPower; + + return { var }; + } + else if (t1->mType == TermTypes::Var && t2->mType == TermTypes::Const) { + auto t1_var = convertToVariable(t1); + auto t2_const = convertToConstant(t2); + + Constant* Const = new Constant(); + Const->mValue = gcf(t1_var->mValue, t2_const->mValue); + return { Const }; + } + else if (t1->mType == TermTypes::Const && t2->mType == TermTypes::Var) { + auto t1_const = convertToConstant(t1); + auto t2_var = convertToVariable(t2); + + Constant* Const = new Constant(); + Const->mValue = gcf(t1_const->mValue, t2_var->mValue); + return { Const }; + } + + return {}; +} + +vector QMReducer::FactorizeTermsToBrack(vector terms, vector terms2) { + vector allTerms = terms; + for (int i = 0; i < terms2.size(); i++) + allTerms.push_back(terms2[i]); + + Term* HCF_all = new Term(); + *HCF_all = *allTerms[0]; + for (int i = 0; i < allTerms.size(); i++) + *HCF_all = *gcdofTerms(HCF_all, allTerms[i])[0]; + + // make first bracket + Bracket* brack1 = new Bracket(); + brack1->setConstant(HCF_all); + for (int i = 0; i < terms.size(); i++) + brack1->mTerms.push_back(Div(terms[i], HCF_all)[0]); + + // make second bracket + Bracket* brack2 = new Bracket(); + brack2->setConstant(HCF_all); + for (int i = 0; i < terms2.size(); i++) + brack2->mTerms.push_back(Div(terms[i], HCF_all)[0]); + + return { brack1, brack2 }; +} + +vector QMReducer::FactorizeTermsToBrack(vector allTerms) { + Term* HCF_all = new Term(); + *HCF_all = *allTerms[0]; + for (int i = 0; i < allTerms.size(); i++) + *HCF_all = *gcdofTerms(HCF_all, allTerms[i])[0]; + + // make bracket + Bracket* brack = new Bracket(); + brack->setConstant(HCF_all); + for (int i = 0; i < allTerms.size(); i++) + brack->mTerms.push_back(Div(allTerms[i], HCF_all)[0]); + + return { brack }; +} + +bool QMReducer::TermsMatch(vector terms1, vector terms2) { + terms1 = OrderTerms(terms1, Identifier_t::_high_order_left); + terms2 = OrderTerms(terms2, Identifier_t::_high_order_left); + return terms1 == terms2; +} + +// WARNING: Make sure that t1 and t2 are in the simplest form. +vector QMReducer::Div(Term * t1, Term * t2, Identifier_t order) { + + + // Check if division is solvable + if (IsDivSolvable(t1, t2)) { + // only two situations + // var / var, pow_no_matter + // const / const, pow_no_matter + + if (t1->mType == TermTypes::Var && t2->mType == TermTypes::Var) { + // var / var + Term* res = new Term(); + res->mValue = t1->mValue / t2->mValue; + res->mPower = t1->mPower - t2->mPower; + if (res->mPower == 0.0) + // it is a constant + res->mType = TermTypes::Const; + else + res->mVariable = t1->mVariable; + return { res }; + } + else if (t1->mType == TermTypes::Const && t2->mType == TermTypes::Const) { + // const / const + t1 = convertToConstant(ReducePower(t1)[0]); + t2 = convertToConstant(ReducePower(t2)[0]); + + Constant* Const = new Constant(); + Const->mValue = t1->mValue / t2->mValue; + return { Const }; + } + } + // if it was a special case + else if (IsDivSpecialCase(t1, t2)) { + // only three situations: + // mult term / mult term + // one term const / one term var + // multi terms / one term + // one term / multi terms + + if (t1->mType == TermTypes::Brack && t2->mType == TermTypes::Brack) { + // multi terms / multi term + auto t1_brack = convertToBracket(t1); + auto t2_brack = convertToBracket(t2); + + // check if taking out common factors makes common bracket terms + auto fraction_nomin = convertToBracket(FactorizeTermsToBrack(t1_brack->mTerms)[0]); + auto fraction_domin = convertToBracket(FactorizeTermsToBrack(t2_brack->mTerms)[0]); + + if (TermsMatch(fraction_nomin->mTerms, fraction_domin->mTerms)) + // common bracket terms matched + // TIME FOR RECURSION! + return Div(fraction_nomin->GetConstant(), fraction_domin->GetConstant()); + + // check if taking out common factors make common bracket coeffiecient + auto fraction = FactorizeTermsToBrack(t1_brack->mTerms, t2_brack->mTerms); + // fraction[0] is nomin + // fraction[1] is domin + fraction_nomin = convertToBracket(fraction[0]); + fraction_nomin->setConstant(nullptr); // Nullptr == 1 + fraction_domin = convertToBracket(fraction[1]); + fraction_domin->setConstant(nullptr); // Nullptr == 1 + + Fraction* frac = new Fraction(); + + frac->mNomin = fraction_nomin->mTerms; + frac->mDomin = fraction_domin->mTerms; + return { frac }; + } + else if (t1->mType == TermTypes::Const && t2->mType == TermTypes::Var) { + // const / var + auto t1_const = convertToConstant(ReducePower(t1)[0]); + auto t2_var = convertToVariable(t2); + + Variable* var = new Variable(); + var->mVariable = t2_var->mVariable; + var->mValue = t1_const->mValue / t2_var->mValue; + var->mPower = t2_var->mPower; + + return { var }; + } + else if (t1->mType == TermTypes::Brack && t2->mType != TermTypes::Brack) { + // multi terms / multi terms + auto t1_brack = convertToBracket(t1); + + vector result; + for (int i = 0; i < t1_brack->mTerms.size(); i++) { + if (t1_brack->mTerms[i]->mType == TermTypes::Op) { + result.push_back(t1_brack->mTerms[i]); + continue; + } + + auto resultant = Div(t1_brack->mTerms[i], t2); + for (auto *term : resultant) result.push_back(term); + } + + return result; + } + else if (t1->mType != TermTypes::Brack && t2->mType == TermTypes::Brack) { + // one term / multi terms + auto t2_Brack = convertToBracket(t2); + + vector result; + for (int i = 0; i < t2_Brack->mTerms.size(); i++) { + if (t2_Brack->mTerms[i]->mType == TermTypes::Op) { + result.push_back(t2_Brack->mTerms[i]); + } + + auto resultant = Div(t1, t2_Brack->mTerms[i]); + for (auto *term : resultant) result.push_back(term); + } + + return result; + } + } + + return {}; +} diff --git a/QuicMaf/maths/solver/term_rewriter/QMReducer.h b/QuicMaf/maths/solver/term_rewriter/QMReducer.h index 571c774..b2f5e1d 100644 --- a/QuicMaf/maths/solver/term_rewriter/QMReducer.h +++ b/QuicMaf/maths/solver/term_rewriter/QMReducer.h @@ -11,6 +11,7 @@ #include "../../terms/Term.h" #include "../../terms/Variable.h" #include "../../terms/Paranthesis.h" +#include "../../terms/Fraction.h" #include "../../tokenizer.h" #include "../../../vendor/lexertk.hpp" @@ -34,7 +35,7 @@ private: // General Utilites vector getRangeOfTerms(vector terms, int begin, int end); Bracket* convertToBracket(Term* t1); - Variable* converToVariable(Term* t1); + Variable* convertToVariable(Term* t1); Constant* convertToConstant(Term* t1); bool IsHigherSig(Term* t1, Term* t2); @@ -49,15 +50,26 @@ private: // General Utilites private: // Power Evaluation vector ReducePower(Term* t1); -public: // Addition Evaluation // TODO: PUBLIC AFTER TESTING +public: // Addition Evaluation // TODO: PRIVATE AFTER TESTING vector Add(Term* t1, Term* t2, Identifier_t order = Identifier_t::_high_order_left); -public: // Subtraction Evaluation // TODO: PUBLIC AFTER TESTING +public: // Subtraction Evaluation // TODO: PRIVATE AFTER TESTING vector Sub(Term* t1, Term* t2, Identifier_t order = Identifier_t::_high_order_left); -public: // Multiplication Evaluation // TODO: PUBLIC AFTER TESTING +public: // Multiplication Evaluation // TODO: PRIVATE AFTER TESTING vector Mul(Term* t1, Term* t2, Identifier_t order = Identifier_t::_high_order_left); +public: // Division Evaluation // TODO: PRIVATE AFTER TESTING + bool IsDivSolvable(Term* t1, Term* t2); + bool IsDivSpecialCase(Term* t1, Term* t2); + + vector gcdofTerms(Term* t1, Term* t2); + vector FactorizeTermsToBrack(vector terms, vector terms2); + vector FactorizeTermsToBrack(vector terms); + + bool TermsMatch(vector terms1, vector terms2); + + vector Div(Term* t1, Term* t2, Identifier_t order = Identifier_t::_high_order_left); private: QMRuleSet mRuleSet; diff --git a/QuicMaf/maths/terms/Fraction.h b/QuicMaf/maths/terms/Fraction.h new file mode 100644 index 0000000..d9a98f9 --- /dev/null +++ b/QuicMaf/maths/terms/Fraction.h @@ -0,0 +1,21 @@ +#ifndef FRACTION_H +#define FRACTION_H +#pragma once +#include "../defines.h" +#include "Term.h" +using namespace std; + +class Fraction: public Term { +public: + Fraction(vector nomin = {}, vector domin = {}) { + Term(); + mNomin = nomin; + mDomin = domin; + mType = TermTypes::Frac; + } + +public: // TODO: try to change to private + vector mNomin; + vector mDomin; +}; +#endif // !CONSTANT_H diff --git a/QuicMaf/maths/tokenizer.h b/QuicMaf/maths/tokenizer.h index d6d464e..e4b228e 100644 --- a/QuicMaf/maths/tokenizer.h +++ b/QuicMaf/maths/tokenizer.h @@ -26,6 +26,7 @@ #include "terms/Term.h" #include "terms/Variable.h" #include "terms/Paranthesis.h" +#include "terms/Fraction.h" #include "../vendor/lexertk.hpp" @@ -163,6 +164,43 @@ static vector combineBrackets(vector terms) { return result; } +static vector combineOpWithTerms(vector terms) { + vector result; + + for (int i = 0; i < terms.size(); i++) { + if (terms[i]->mType == TermTypes::Op) { + // operator detected + auto op = static_cast(terms[i]); + if (op->mOperator == '/') { + // division found + + // check if it was the first + if (i == 0) { + // invalid + cout << "Can't divide with a Nill nominator!" << endl; + system("PAUSE"); + exit(0); + } + + + Fraction* frac = new Fraction(); + frac->mDomin.push_back(terms[i + 1]); + frac->mNomin.push_back(terms[i - 1]); + result.pop_back(); // pop the nominator, before push + result.push_back(frac); + + // consume the dominator + i += 1; + continue; + } + } + result.push_back(terms[i]); + continue; + } + + return result; +} + static vector tokenize(lexertk::generator lexed) { vector result; @@ -321,8 +359,9 @@ static vector tokenize(lexertk::generator lexed) { i = tok.end; // no need to increment, automatically done in loop statment } + // post-fix fixes result = combineBrackets(result); - + result = combineOpWithTerms(result); return result; } #endif // !TOKENIZER_H diff --git a/bin/Debug-x64/QuicMaf.exe b/bin/Debug-x64/QuicMaf.exe index 9d37baa..052f193 100644 Binary files a/bin/Debug-x64/QuicMaf.exe and b/bin/Debug-x64/QuicMaf.exe differ diff --git a/bin/Debug-x64/QuicMaf.ilk b/bin/Debug-x64/QuicMaf.ilk index a82b6ea..04d13b6 100644 Binary files a/bin/Debug-x64/QuicMaf.ilk and b/bin/Debug-x64/QuicMaf.ilk differ diff --git a/bin/Debug-x64/QuicMaf.pdb b/bin/Debug-x64/QuicMaf.pdb index 183f35e..b5d9816 100644 Binary files a/bin/Debug-x64/QuicMaf.pdb and b/bin/Debug-x64/QuicMaf.pdb differ