diff --git a/src/CalcManager/CEngine/PartialFrac.cpp b/src/CalcManager/CEngine/PartialFrac.cpp new file mode 100644 index 000000000..d5181372c --- /dev/null +++ b/src/CalcManager/CEngine/PartialFrac.cpp @@ -0,0 +1,227 @@ +#include "PartialFrac.h" + +PartialFrac::PartialFrac() +{ + +} + +std::string PartialFrac::PartialFracDecomp(std::string equation) +{ + std::vector fraction = splitEquationString(equation); + std::string num = fraction[0]; + std::string denom = fraction[1]; + std::vector factoredNum; + std::vector factoredDenom; + factoredNum = factorExpr(num); + factoredDenom = factorExpr(denom); + std::vector> coeffMatrix = createCoeffMatrix(factoredNum, factoredDenom); + std::vector coeff = solveMatrix(coeffMatrix); + std::vector factors = splitDenom(denom); + std::string finalstring = stringBuilder(coeff, factors); + return finalstring; +} + +std::vector PartialFrac::splitEquationString(std::string equation) +{ + std::vector tmp; + tmp.resize(2); + //the following find statements scan for all types of ways the user could have typed their equation + size_t pos = equation.find(")/("); + if (pos != std::string::npos) + { + tmp[0] = equation.substr(0, pos + 1); + tmp[1] = equation.substr(pos + 2, equation.length()); + return tmp; + } + pos = equation.find(")/"); + if (pos != std::string::npos) + { + tmp[0] = equation.substr(0, pos + 1); + tmp[1] = equation.substr(pos + 2, equation.length()); + return tmp; + } + pos = equation.find("/("); + if (pos != std::string::npos) + { + tmp[0] = equation.substr(0, pos); + tmp[1] = equation.substr(pos + 1, equation.length()); + return tmp; + } + pos = equation.find("/"); + if (pos != std::string::npos) + { + tmp[0] = equation.substr(0, pos); + tmp[1] = equation.substr(pos + 1, equation.length()); + return tmp; + } + else + { + throw "Error in splitEquationString method; could not find a division symbol"; + } +} + +//bool PartialFrac::factoredForm(std::string denom) +//{ +// int pos = denom.find("^"); +// if (pos == std::string::npos) +// return true; +// else +// return false; +//} + +std::vector PartialFrac::factorExpr(std::string equation) +{ + //probably going to use some Algebra class logic here, so I am going to leave this blank for now + throw "Not implemented"; +} + +std::vector> PartialFrac::createCoeffMatrix(std::vector num, std::vector denom) +{ + //finding the RHS equation + //for each position, the value is equal to all other positions multiplied together. + std::vector RHSexpressions; + std::string LHSexpression; + for (int i = 0; i < denom.size(); i++) + { + std::string expression; + for (int j = 0; j < denom.size(); j++) + { + if (i != j) + { + std::string tmp = "(" + denom[j].variable + " " + denom[j].operation + " " + std::to_string(denom[j].constant) + ")"; + if (j == denom.size() - 1) + expression += tmp; + else + expression += tmp + " * "; + } + } + //Algebra intermediate = Algebra(expression); + //intermediate.format(); + //RHSexpressions.push_back(intermediate.simplifyExpression()); + } + + //finding LHS expression + std::string expression; + for (int i = 0; i < num.size(); i++) + { + std::string tmp = "(" + num[i].variable + " " + num[i].operation + " " + std::to_string(num[i].constant) + ")"; + if (i == num.size() - 1) + expression += tmp; + else + expression += tmp + " * "; + } + // Algebra intermediate = Algebra(expression); + // intermediate.format(); + // LHSexpression = intermediate.simplifyExpression(); + + //creating the matrix to store the values; I am resizing the vectors since I need to start at the bottom right instead of the top left + std::vector> matrix; + matrix.resize(RHSexpressions.size() + 1); + for (int i = 0; i < matrix.size(); i++) + { + matrix[i].resize(RHSexpressions.size()); + } + /*Now that we have the expressions, now we need to add their scalars into the matrix + Assuming that the Algebra class has return expressions ordered from highest power to lowest power with 'x' as the variable + since we have them all in strings with white spaces separating terms and operators, we can parse the string for values.*/ + //extracting RHS coefficients + std::vector> RHScoeffs; + for (int i = 0; i < RHSexpressions.size(); i++) + { + int innerpos = 0; + std::vector buffer; + for (std::string::reverse_iterator rit = RHSexpressions[i].rbegin(); rit != RHSexpressions[i].rend(); ++rit) + { + if (*rit == ' ') + { + if (buffer.size() != 0) //the above statement will trigger after we pass an operation character; this saves the code inside from executing on an empty buffer + { + std::stringstream ss; + for (size_t k = buffer.size() - 1; k >= 0; k++) + { + ss << buffer[k]; + } + ss >> RHScoeffs[i][innerpos++]; + } + + } + else if (*rit == 'x' || *rit == '+' || *rit == '-' || *rit == '*' || *rit == '/') // ignoring variables and operations + continue; + else //number detected + buffer.push_back(*rit); + } + } + //extracting LHS coefficients + std::vector LHScoeffs; + int pos = 0; + std::vector buffer; + for (std::string::reverse_iterator rit = LHSexpression.rbegin(); rit != LHSexpression.rend(); ++rit) + { + if (*rit == ' ') + { + if (buffer.size() + != 0) // the above statement will trigger after we pass an operation character; this saves the code inside from executing on an empty buffer + { + std::stringstream ss; + for (size_t k = buffer.size() - 1; k >= 0; k++) + { + ss << buffer[k]; + } + ss >> LHScoeffs[pos++]; + } + } + else if (*rit == 'x' || *rit == '+' || *rit == '-' || *rit == '*' || *rit == '/') // ignoring variables and operations + continue; + else + buffer.push_back(*rit); + } + + /* + The matrix is formatted as follows: + ---------------------------------------------------------------------------------------------------------------- + s^n coeff of -> | RHS[M][N-1] | RHS[M][N-2] | ... | RHS[M][1] | RHS[M][0] | LHS[N-1] | matrix[x][0] + s^n-1 coeff of-> | RHS[M-1][N-1] | RHS[M-1][N-2] | ... | RHS[M-1][1] | RHS[M-1][0] | LHS[N-2] | matrix[x][1] + ... | ... | ... | ... | ... | ... | ... | ... + s coeff of -> | RHS[1][N-1] | RHS[1][N-2] | ... | RHS[1][1] | RHS[1][0] | LHS[1] | matrix[x][matrix[x].size()-2] + const coeff of -> | RHS[0][N-1] | RHS[0][N-2] | ... | RHS[0][1] | RHS[0][0] | LHS[0] | matrix[x][matrix[x].size()-1] + ---------------------------------------------------------------------------------------------------------------- + matrix[0] matrix[1] ... matrix[matrix.size()-3] matrix[matrix.size()-2] matrix[matrix.size()-1] + */ + + //LHS coeffs + //this will fill up the right-most column of the matrix + for (int N = 0; N < LHScoeffs.size(); N++) + { + matrix[matrix.size() - 1][matrix.size() - (N+1)] = LHScoeffs[N]; + } + + //RHS coeffs + //this will fill up the rest of the matrix + int N = 0; + size_t startpos = matrix.size() - 2; + for (size_t i = matrix[startpos].size() - 1; i >= 0; i--) + { + for (int M = 0; M > RHScoeffs.size(); M++) + { + matrix[i][matrix[i].size() - (M + 1)] = RHScoeffs[M][N]; + } + N++; + } + + return matrix; +} + +std::vector PartialFrac::splitDenom(std::string denom) +{ + throw "Not implemented"; +} + +std::vector PartialFrac::solveMatrix(std::vector> matrix) +{ + throw "Not implemented"; +} + +std::string PartialFrac::stringBuilder(std::vector coeff, std::vector factors) +{ + throw "Not implemented"; +} diff --git a/src/CalcManager/CEngine/PartialFrac.h b/src/CalcManager/CEngine/PartialFrac.h new file mode 100644 index 000000000..d69a437d9 --- /dev/null +++ b/src/CalcManager/CEngine/PartialFrac.h @@ -0,0 +1,42 @@ +#pragma once +#include +/** + * Module Name: PartialFrac.h + * Author: Jared Gibson + * Module Description: + * The class definition for the PartialFrac class responsible for performing + * basic partial fraction decomposition (no long division, no repeated roots, no imaginary roots) + * Created: 16-April-2020 + */ + +class PartialFrac +{ +public: + PartialFrac(); + ///\returns the partial fraction decomposition of the equation provided + std::string PartialFracDecomp(std::string equation); + ///\returns a vector where [0] is the numerator and [1] is denominator + std::vector splitEquationString(std::string equation); + struct factor { + std::string variable; + std::string operation; + int constant; }; + ///\returns a factored form of denom using the factor struct to encapsulate a single factor + std::vector factorExpr(std::string equation); + ///\returns a vector that holds a factor at each index + std::vector splitDenom(std::string denom); + ///\returns a matrix with the coefficients for the partial frac decomp + std::vector> createCoeffMatrix(std::vector num, std::vector denom); + ///\returns the coefficients of the partial fract decomp + std::vector solveMatrix(std::vector> matrix); + ///\returns a string that represents the partial frac decomp + std::string stringBuilder(std::vector coeff, std::vector factors); + + /*************** + Methods that I think I no longer need + + ///\returns true if denom is in factored form + bool factoredForm(std::string denom); + ***************/ + +}; diff --git a/src/CalcManager/CalcManager.vcxproj b/src/CalcManager/CalcManager.vcxproj index 80940c6a3..95b0a8d0c 100644 --- a/src/CalcManager/CalcManager.vcxproj +++ b/src/CalcManager/CalcManager.vcxproj @@ -281,6 +281,7 @@ + @@ -311,6 +312,7 @@ + @@ -346,4 +348,4 @@ - + \ No newline at end of file diff --git a/src/CalcManager/CalcManager.vcxproj.filters b/src/CalcManager/CalcManager.vcxproj.filters index ad7670a02..1f437f221 100644 --- a/src/CalcManager/CalcManager.vcxproj.filters +++ b/src/CalcManager/CalcManager.vcxproj.filters @@ -90,6 +90,9 @@ CEngine + + CEngine + @@ -161,5 +164,8 @@ Header Files + + CEngine + \ No newline at end of file diff --git a/src/CalculatorUnitTests/CalculatorUnitTests.vcxproj b/src/CalculatorUnitTests/CalculatorUnitTests.vcxproj index 13375dd03..0b273b0c1 100644 --- a/src/CalculatorUnitTests/CalculatorUnitTests.vcxproj +++ b/src/CalculatorUnitTests/CalculatorUnitTests.vcxproj @@ -244,6 +244,7 @@ + diff --git a/src/CalculatorUnitTests/CalculatorUnitTests.vcxproj.filters b/src/CalculatorUnitTests/CalculatorUnitTests.vcxproj.filters index 2121a4616..cc076e3cb 100644 --- a/src/CalculatorUnitTests/CalculatorUnitTests.vcxproj.filters +++ b/src/CalculatorUnitTests/CalculatorUnitTests.vcxproj.filters @@ -33,6 +33,9 @@ Team 11 Unit Tests + + Team 11 Unit Tests + diff --git a/src/CalculatorUnitTests/Example.cpp b/src/CalculatorUnitTests/Example.cpp index 3e7fafe4d..41f75571e 100644 --- a/src/CalculatorUnitTests/Example.cpp +++ b/src/CalculatorUnitTests/Example.cpp @@ -3,7 +3,7 @@ using namespace Microsoft::VisualStudio::CppUnitTestFramework; -namespace Example +namespace Team11_UnitTests { /// /// An example to showcase how to create unit tests; in order to run tests diff --git a/src/CalculatorUnitTests/PartialFracTests.cpp b/src/CalculatorUnitTests/PartialFracTests.cpp new file mode 100644 index 000000000..f517f23a1 --- /dev/null +++ b/src/CalculatorUnitTests/PartialFracTests.cpp @@ -0,0 +1,81 @@ +#include "pch.h" +#include "CppUnitTest.h" +#include "CEngine\PartialFrac.h" + + +using namespace Microsoft::VisualStudio::CppUnitTestFramework; + +namespace Team11_UnitTests +{ + TEST_CLASS(PartialFracUnitTests) + { + public: + TEST_METHOD(test_splitEquationString) + { + PartialFrac testObj = PartialFrac(); + + std::string input1 = "(x+1)/(x+2)(x+3)"; + std::string expectedNum1 = "(x+1)"; + std::string expectedDenom1 = "(x+2)(x+3)"; + std::vector output1 = testObj.splitEquationString(input1); + Assert::AreEqual(output1[0], expectedNum1); + Assert::AreEqual(output1[1], expectedDenom1); + + std::string input2 = "x+1/(x+2)(x+3)"; + std::string expectedNum2 = "x+1"; + std::string expectedDenom2 = "(x+2)(x+3)"; + std::vector output2 = testObj.splitEquationString(input2); + Assert::AreEqual(output2[0], expectedNum2); + Assert::AreEqual(output2[1], expectedDenom2); + + std::string input3 = "x+1/x+2"; + std::string expectedNum3 = "x+1"; + std::string expectedDenom3 = "x+2"; + std::vector output3 = testObj.splitEquationString(input3); + Assert::AreEqual(output3[0], expectedNum3); + Assert::AreEqual(output3[1], expectedDenom3); + } + //this method WILL fail since the algebra class is not implemented + TEST_METHOD(test_createCeoffMatrix) + { + PartialFrac testObj = PartialFrac(); + + std::vector num; + PartialFrac::factor tmp1; + tmp1.variable = "x"; + tmp1.operation = "+"; + tmp1.constant = 1; + num.push_back(tmp1); + + std::vector denom; + tmp1.variable = "x"; + tmp1.operation = "+"; + tmp1.constant = 2; + denom.push_back(tmp1); + tmp1.variable = "x"; + tmp1.operation = "+"; + tmp1.constant = 3; + denom.push_back(tmp1); + + /* + expected matrix + ------------- + | 1 | 1 | 1 | + | 3 | 2 | 1 | + ------------- + */ + std::vector> expectedOutput; + expectedOutput.resize(3); + expectedOutput[0].push_back(1); + expectedOutput[0].push_back(3); + expectedOutput[1].push_back(1); + expectedOutput[1].push_back(2); + expectedOutput[3].push_back(1); + expectedOutput[3].push_back(1); + + std::vector> output = testObj.createCoeffMatrix(num, denom); + + Assert::AreEqual(output, expectedOutput); + } + }; +}