Main Page | Class Hierarchy | Alphabetical List | Class List | Directories | File List | Class Members | File Members | Related Pages

PolySolver.cpp

Go to the documentation of this file.
00001 
00002 // MathCore = a WYSIWYG equation editor + a powerful math engine     //
00003 // Copyright (C) 2003 by Francesco Montorsi                          //
00004 //                                                                   //
00005 // This library is free software; you can redistribute it and/or     //
00006 // modify it under the terms of the GNU Lesser General Public        //
00007 // License as published by the Free Software Foundation; either      //
00008 // version 2.1 of the License, or (at your option) any later         //
00009 // version.                                                          //
00010 //                                                                   //
00011 // This library is distributed in the hope that it will be useful,   //
00012 // but WITHOUT ANY WARRANTY; without even the implied warranty of    //
00013 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the      //
00014 // GNU Lesser General Public License for more details.               //
00015 //                                                                   //
00016 // You should have received a copy of the GNU Lesser General Public  //
00017 // License along with this program; if not, write to the Free        //
00018 // Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,   //
00019 // MA 02111-1307, USA.                                               //
00020 //                                                                   //
00021 // For any comment, suggestion or feature request, please contact    //
00022 // the administrator of the project at frm@users.sourceforge.net     //
00023 //                                                                   //
00030 
00031 
00032 
00033 // optimization for GCC compiler
00034 #ifdef __GNUG__
00035 #pragma implementation "PolySolver.h"
00036 #endif
00037 
00038 // includes
00039 #include "mc/mcprec.h"
00040 #ifdef __BORLANDC__
00041     #pragma hdrstop
00042 #endif
00043 
00044 #ifndef mcPRECOMP
00045 /* #include "mc/PolySolver.h"
00046  #include "mc/MathAndSystem.h"
00047  #include "mc/MathOrSystem.h"
00048  #include "mc/MathLine.h"
00049  #include "mc/Monomial.h"
00050  #include "mc/Polynomial.h"
00051  #include "mc/Fraction.h"
00052  #include "mc/Radical.h"
00053  #include "mc/Number.h"
00054  #include "mc/Symbol.h"
00055  #include "mc/Bracket.h" */
00056  #include "mc/mc.h"
00057 #endif
00058 
00059 
00060 
00061 
00062  // a little useful macro
00063 #define mcPOLYSOLVLOG(x)  mcMATHLOG(wxT("mcPolySolver::SolveLineFirstDegree [") + \
00064          steps.data_GetLast()->io_GetInlinedExpr() + wxT("] - ") x);
00065 
00066 
00067 
00068 
00069 // ----------------------------------------
00070 // mcPOLYSOLVER
00071 // ----------------------------------------
00072 
00073 mcPolySolver::mcPolySolver()
00074 {
00075  m_strName = wxT("Polynomial equation solver");
00076  m_strDesc = 
00077   wxT("Solves polynomial equations of first degree, ")
00078   wxT("trying to do the minimum possible number ")
00079   wxT("of operations.");
00080  m_nType = mcST_POLYNOMIAL;  
00081  
00082  math_ResetCoeffArr();
00083 }
00084 
00085 void mcPolySolver::math_ResetCoeffArr()
00086 {
00087  for (int i=0; i < mcPOLYSOLVER_MAXDEGREE; i++)
00088   m_pCoeff[i] = mcEmptyElement;
00089 }
00090 
00091 void mcPolySolver::math_DeleteCoeffArr()
00092 {
00093  math_ResetCoeffArr();
00094  //for (int i=0; i < mcPOLYSOLVER_MAXDEGREE; i++)
00095   //mcSAFE_DELETE(m_pCoeff[i]);
00096 }
00097 
00098 bool mcPolySolver::math_WorksOn(const mcMathOrSystem &m) const
00099 {
00100  if (m.math_GetMathSystemType().m_tMath1 != mcMSTL1_EQUATIONS)
00101   return FALSE;
00102 
00103  mcMathType type = m.math_GetMathType();
00104  if (type.m_tMath1 != mcMTL1_POLYNOMIAL || 
00105   type.m_tMath2 != mcMTL2_ALGEBRAIC)
00106   return FALSE;
00107 
00108  // mcPolySolver accepts both parametric and 
00109  // non-parametric linear systems...
00110  return TRUE;
00111 }
00112 
00113 bool mcPolySolver::math_SolveLine(mcMathLine &p, const mcSymbolProperties *unk,
00114          mcSystemStepArray &steps)
00115 {
00116  // no unknowns set by mcSolver::math_PreSolve ?
00117  mcASSERT(unk != NULL, wxT("we need the main unknown !!"));
00118  mcLOG(wxT("mcPolySolver:math_SolveLine - The unknown is [%s]."), mcTXTP(unk));
00119 
00120  // first of all, detect the degree of the given line
00121  mcMATHLOG(wxT("mcPolySolver::SolveLine - I'm detecting the equation degree..."));
00122 
00123  mcIntegerValue degree = p.math_GetMaxDegreeFor(unk);
00124  if (!degree.isValid() || degree < 0) {
00125   m_strLastErr = wxT("Cannot retrieve the maximum degree for ") + unk->io_GetInlinedSym();
00126   return FALSE;
00127  }
00128 
00129  if (degree > mcPOLYSOLVER_MAXDEGREE) {
00130   m_strLastErr = wxT("Cannot solve an equation with degree > 10 for the unknown ") + 
00131       unk->io_GetInlinedSym();
00132   return FALSE;
00133  }
00134 
00135  // is this a constant expression ?
00136  mcASSERT(degree != 0, wxT("The mcPolySolver::math_WorksOn should have returned FALSE !!"));
00137 
00138  // ok, we are ready to start !
00139  mcMATHLOG(wxT("mcPolySolver::math_SolveLine - The line [%s] has degree [%d] for [%s]."),
00140     mcTXT(p), degree.GetInt(), mcTXTP(unk));
00141 
00142  bool res = TRUE;
00143  switch (degree.GetInt()) {
00144  case 1:
00145   res = math_SolveLineFirstDegree(p, unk, steps);   
00146   break;
00147 
00148  case 2:
00149   res = math_SolveLineSecondDegree(p, unk, steps);
00150   break;
00151  }
00152  
00153  // cleanup
00154  math_DeleteCoeffArr();
00155  steps.data_Check();
00156 
00157  // TRUE if we managed to solve the data...
00158  return res;
00159 }
00160 
00161 void mcPolySolver::math_FindPolyCoeff(const mcMathLine &equ, const mcSymbolProperties *unk, 
00162            int degree)
00163 {
00164  // be sure coeff arr is clean
00165  math_DeleteCoeffArr();
00166 
00167  // search in the left member
00168  for (int i=0; i < degree; i++) {
00169 
00170   // this works only on the left member...
00171   m_pCoeff[i] = equ.data_GetLeftMem().math_GetCoeffOf(unk, i);
00172  }
00173 }
00174 
00175 void mcPolySolver::math_GetPolyCoeff(mcMathLine &curr,
00176           const mcSymbolProperties *unk,
00177           mcSystemStepArray &steps)
00178 {
00179  //mcMathMng *curr = math_GetNewStep(p);
00180  curr.data_Check();
00181 
00182  // STEP #1: move undesidered symbols to the right member
00183  //          and desired ones to the left
00184  mcMATHLOG(wxT("mcPolySolver::math_GetPolyCoeff - moving symbols"));
00185  /*curr.math_MoveNonSymbol(FALSE);
00186  curr.math_MoveParameters(FALSE);
00187  curr.math_MoveConstants(FALSE);*/
00188  curr.math_MoveFreeFrom(unk, FALSE);
00189  curr.math_MoveSymbol(unk, TRUE);
00190  steps.data_AddLine(curr);
00191 
00192  // STEP #2: simplify the data...
00193  mcPOLYSOLVLOG(wxT("simplifying"));
00194  math_DoCompleteSimplification(mcEXPSIM_NOFLAGS, mcEXPSIM_NOFLAGS, curr, steps); 
00195  
00196  // STEP #3: move symbols again
00197  mcPOLYSOLVLOG(wxT("moving symbols again"));
00198  //curr = math_GetNewStep(curr);
00199  curr.math_MoveNonSymbol(FALSE);
00200  curr.math_MoveParameters(FALSE);
00201  curr.math_MoveConstants(FALSE);
00202  curr.math_MoveSymbol(unk, TRUE);
00203  steps.data_AddLine(curr);
00204 
00205  // STEP #4: simplify the data again...
00206  mcPOLYSOLVLOG(wxT("simplifying")); 
00207  math_DoCompleteSimplification(mcEXPSIM_NOFLAGS, mcEXPSIM_NOFLAGS, curr, steps);
00208 
00209  // STEP #5: factorize all the monomials containing the unknown
00210  mcPOLYSOLVLOG(wxT("factorizing"));
00211  //curr = math_GetNewStep(curr);
00212  curr.math_FactoreOut(unk, *mcPolynomialHelpers::smath_pOne, FALSE);
00213  curr.math_FactoreOutFreeOf(unk, mcEmptyPolynomial, TRUE);
00214  steps.data_AddLine(curr);
00215 
00216  // STEP #6: simplify the data again...
00217  mcPOLYSOLVLOG(wxT("simplifying")); 
00218  math_DoCompleteSimplification(mcEXPSIM_KEEP_FACTORIZATION,mcEXPSIM_KEEP_FACTORIZATION, curr, steps);
00219 
00220  // get copies of the coefficients of the unknown for each degree 
00221  mcPOLYSOLVLOG(wxT("searching the coefficients of the unknown"));
00222  math_FindPolyCoeff(curr, unk, 2);
00223 }
00224 
00225 bool mcPolySolver::math_SolveLineFirstDegree(mcMathLine &p, 
00226             const mcSymbolProperties *unk,
00227             mcSystemStepArray &steps)
00228 {
00229  // okay; we can start with the real resolution of the given data,
00230  // storing all the steps in the given array... 
00231  math_GetPolyCoeff(p, unk, steps);
00232 
00233  if (m_pCoeff[1] == mcEmptyMonomial)
00234   return TRUE;
00235  //mcPOLYSOLVLOG(wxT("the coeff #0 is [") + wxString(mcTXT(m_pCoeff[0])) + "]");
00236  mcPOLYSOLVLOG(wxT("the coeff #1 is [") + wxString(mcTXT(m_pCoeff[1])) + wxT("]"));
00237 
00238  // STEP #6: divide everything by the coefficient of the x (if it's not 1)
00239  if (!m_pCoeff[1].math_isWrappingOnly(1.0)) {
00240   //curr = math_GetNewStep(curr);
00241   p.math_SimpleDivideBy(mcPolynomial(m_pCoeff[1]));
00242   steps.data_AddLine(p);
00243  }
00244 
00245  // STEP #7: simplify the data again...
00246  mcPOLYSOLVLOG(wxT("I'm beginning last steps"));
00247  math_DoCompleteSimplification(mcEXPSIM_NOFLAGS, mcEXPSIM_NOFLAGS, p, steps);
00248  steps.data_AddKeyLine(p);
00249 
00250  return TRUE;
00251 }
00252 
00253 
00254 bool mcPolySolver::math_SolveLineSecondDegree(mcMathLine &curr, 
00255              const mcSymbolProperties *unk,
00256              mcSystemStepArray &steps)
00257 {
00258  //mcMathMng *curr = math_GetNewStep(p);
00259  curr.data_Check();
00260 
00261  curr.math_FactoreOut(unk, *mcPolynomialHelpers::smath_pOne, FALSE);
00262  curr.math_FactoreOutFreeOf(unk, mcEmptyPolynomial, TRUE);
00263 
00264  // we need to have the coefficients of the unknown:
00265  //
00266  //               a*x^2 + b*x + c
00267  //
00268  mcPOLYSOLVLOG(wxT("searching the coefficients of the unknown"));
00269  math_FindPolyCoeff(curr, unk, 3);
00270  mcASSERT(m_pCoeff[2] != mcEmptyMonomial, wxT("This is not a 2nd degree equation !"));
00271 
00272  // we will call "a" the coeff of the x^2
00273  // wxT("b") the coeff of x^1 and "c" the coeff of x^0
00274  mcMonomial a(m_pCoeff[2]), b(m_pCoeff[1]), c(m_pCoeff[0]);
00275  mcPOLYSOLVLOG(wxT("the coeff #2 is [") + wxString(mcTXT(a)) + wxT("]"));
00276 
00277  // classify this type of 2nd degree equ
00278  int type = 3;  // complete by default (a,b,c != 0)
00279  if (b == mcEmptyMonomial && c == mcEmptyMonomial) type=0;
00280  if (b == mcEmptyMonomial && c != mcEmptyMonomial) type=1;
00281  if (b != mcEmptyMonomial && c == mcEmptyMonomial) type=2;
00282 
00283  switch (type) {
00284  case 0:
00285   {
00286    // the equation is in the form    ax^2 = 0
00287    // (both b and c coefficients does not exist)
00288    
00289    // we've got two coincident solutions
00290    curr.data_GetLeftMem().data_DeleteAll();
00291    curr.data_GetLeftMem().math_WrapSymbol(unk);
00292    steps.data_AddKeyLine(curr);
00293 
00294    return TRUE;
00295   }
00296 
00297  case 1:
00298   {
00299    // the equation is in the form    ax^2 + c = 0
00300    // (the b coefficient does not exist)
00301    
00302    // just move c to the right member   
00303    curr.math_MoveFreeFrom(unk, FALSE);   
00304    steps.data_AddLine(curr);
00305 
00306    // divide everything by a
00307    mcPOLYSOLVLOG(wxT("dividing")); 
00308    curr.math_DivideBy(mcPolynomial(a), NULL);
00309    steps.data_AddLine(curr);
00310 
00311    // simplify
00312    mcPOLYSOLVLOG(wxT("simplifying"));
00313    math_DoCompleteSimplification(mcEXPSIM_NOFLAGS, mcEXPSIM_NOFLAGS, curr, steps);
00314 
00315    // extract the square root
00316    curr.math_SimpleRaiseTo(mcPolynomial(mcFraction(mcNumber(1), mcNumber(2))));
00317 
00318    // simplify
00319    mcPOLYSOLVLOG(wxT("simplifying"));
00320    math_DoCompleteSimplification(mcEXPSIM_NOFLAGS, mcEXPSIM_NOFLAGS, curr, steps);
00321 
00322 
00323    // now, extract the roots into two different equations:
00324    //
00325    //          x = SQRT(c/a)    OR     x = -SQRT(c/a)
00326    //
00327    mcPOLYSOLVLOG(wxT("creating the OR system with the two solutions")); 
00328    mcPolynomial tmp(curr.data_GetRightMem());
00329    mcPolynomial left(unk);
00330    
00331    //mcMathLine equ1(left, tmp);
00332    //tmp.math_ChangeAllSigns();
00333    //mcMathLine equ2((const mcPolynomial &)left, tmp);
00334    math_SplitInTwoEquations(left, tmp, left, -mcPolynomial(tmp), steps);
00335    
00336    // add the final step
00337    //mcMathOrSystem *last = steps.data_GetLast();
00338    //steps.data_AddFinalSys(new mcMathOrSystem(equ1, equ2));
00339    math_DoCompleteSimplification(mcEXPSIM_NOFLAGS, mcEXPSIM_NOFLAGS, curr, steps);
00340 
00341    return TRUE;
00342   }
00343   
00344  case 2:
00345   {
00346    // the equation is in the form    ax^2 + bx = 0
00347    // (the c coefficient does not exist)
00348 
00349    // one solution is always zero...
00350    curr.math_FactoreOutAll(FALSE);
00351    mcASSERT(curr.math_isFactorized(), wxT("Something wrong"));
00352    steps.data_AddLine(curr);
00353 
00354    // remove from curr the "x" factor"
00355    mcArrayEntry idx = curr.data_GetLeftMem().
00356     math_GetIndexOf(0, mcMonomial(mcSymbol(unk)), TRUE);
00357    curr.data_GetLeftMem().math_Remove(
00358     curr.data_GetLeftMem().math_GetMonomialIdxFromEntry(idx), FALSE);
00359 
00360    // now, we can split the equations in two parts...
00361    mcMathLine solved(unk, mcRealValue(0.0)); // the solved one (x=0)   
00362    math_SplitInTwoEquations(solved, curr, steps);
00363    
00364    // we'll now proceed with resolution with a step array...
00365    mcSystemStepArray semisteps1, semisteps2;
00366 
00367    // the first equation is already solved; it is "x=0"
00368    semisteps1.data_AddLine(solved);
00369    
00370    // apply 1st degree solver on the second equation
00371    mcPOLYSOLVLOG(wxT("final solving of second equation")); 
00372    if (!math_SolveLineFirstDegree(curr, unk, semisteps2))
00373     return FALSE;
00374    mcMATHLOG(wxT("\n\n%s\n\n"), mcTXT(semisteps2));
00375    
00376    // now, merge the two step arrays into the original one...
00377    steps.math_AppendCombinationOf(semisteps1, semisteps2, mcLO_OR);
00378 
00379    return TRUE;
00380   }
00381   
00382  case 3:
00383   {
00384    // the equation is in the form    ax^2 + bx + c = 0
00385    mcPOLYSOLVLOG(wxT("the coeff #1 is [") + wxString(mcTXT(b)) + wxT("]"));
00386    mcPOLYSOLVLOG(wxT("the coeff #0 is [") + wxString(mcTXT(c)) + wxT("]"));
00387    
00388    // STEP #1: multiply by 4a
00389    mcPOLYSOLVLOG(wxT("multiplying by 4a"));
00390    mcPolynomial tmp(4);
00391    tmp *= a;
00392    curr *= tmp;
00393    steps.data_AddKeyLine(curr);
00394    
00395    // STEP #1b: simplify the data again...
00396    mcPOLYSOLVLOG(wxT("simplifying"));
00397    math_DoCompleteSimplification(mcEXPSIM_NOFLAGS,mcEXPSIM_NOFLAGS, curr, steps);
00398    
00399    // STEP #2: add & subtract b^2 to the left member
00400    mcPOLYSOLVLOG(wxT("adding and subtracting b^2"));
00401    //curr = math_GetNewStep(curr);
00402    curr.data_GetLeftMem() += mcPolynomial(b)^2;
00403    curr.data_GetLeftMem() -= mcPolynomial(b)^2;
00404    steps.data_AddKeyLine(curr);
00405    
00406    // STEP #3: pick up as a quadratic binomial the three terms:
00407    //
00408    //              4a^2x^2+4abx+b^2 = (2ax+b)^2
00409    //
00410    mcPOLYSOLVLOG(wxT("picking up the quadratic"));
00411    //curr = math_GetNewStep(curr);
00412    
00413    mcPolynomial &l = curr.data_GetLeftMem();
00414    mcSymbol sym((mcSymbolProperties *)unk);
00415    
00416    mcArrayEntry idx1 = l.math_GetIndexOf(0, mcMonomial(b)^2, TRUE);
00417    l.math_Remove(l.math_GetMonomialIdxFromEntry(idx1), TRUE);
00418    
00419    mcMonomial square(a^2);
00420    square *= mcNumber(4);
00421    square *= mcMonomial(sym^mcPolynomial(mcNumber(2)));
00422    mcArrayEntry idx2 = l.math_GetIndexOf(0, square, TRUE);
00423    l.math_Remove(l.math_GetMonomialIdxFromEntry(idx2), TRUE);
00424    
00425    mcMonomial product(a*b);
00426    product *= mcNumber(4);
00427    product *= sym;
00428    mcArrayEntry idx3 = l.math_GetIndexOf(0, product, TRUE);
00429    l.math_Remove(l.math_GetMonomialIdxFromEntry(idx3), TRUE);
00430    
00431    // build a bracket containing 2ax+b as contents 
00432    mcPolynomial contents(2);
00433    contents *= a;
00434    contents *= sym;
00435    contents += b;
00436    mcBracket br(contents);
00437    br ^= mcPolynomial(mcNumber(2));  // and 2 as exponent
00438    curr.math_MoveAll(FALSE);
00439    curr.data_GetLeftMem() += mcPolynomial(br);
00440    steps.data_AddKeyLine(curr);
00441   
00442    // apply simplification
00443    mcPOLYSOLVLOG(wxT("simplifying"));
00444    math_DoCompleteSimplification(mcEXPSIM_KEEP_FACTORIZATION, mcEXPSIM_KEEP_FACTORIZATION, curr, steps);
00445    
00446    // STEP #4: extract the square root creating two mcMathLines joined by an OR op:
00447    // 
00448    //                             (2ax+b)^2 = b^2 - 4ac
00449    //
00450    //               2ax+b = SQRT(b^2 - 4ac)  OR  2ax+b = -SQRT(b^2 - 4ac)
00451    //
00452    mcPOLYSOLVLOG(wxT("applying the square root"));
00453    //curr = math_GetNewStep(curr);
00454    curr.math_SimpleRaiseTo(mcPolynomial(mcFraction(mcNumber(1), mcNumber(2))));
00455    
00456    
00457    mcMathLine equ1(curr), equ2(curr);
00458    equ2.data_GetRightMem().math_ChangeAllSigns();
00459    mcMathOrSystem *step = new mcMathOrSystem();
00460    
00461    // *we* must own equ1 and equ2, so give a copy to mcMathOrSystem
00462    step->data_AddLineToNewAndSystem(new mcMathLine(equ1)); 
00463    step->data_AddLineToNewAndSystem(new mcMathLine(equ2));
00464    steps.data_AddSys(step);
00465    
00466    // we'll now proceed with resolution with two step array...
00467    mcSystemStepArray semisteps1, semisteps2;
00468    
00469    // apply 1st degree solver on the first equation
00470    mcPOLYSOLVLOG(wxT("final solving of first equation")); 
00471    if (!math_SolveLineFirstDegree(equ1, unk, semisteps1))
00472     return FALSE;
00473    mcMATHLOG(wxT("\n\n%s\n\n"), mcTXT(semisteps1));
00474    
00475    // apply 1st degree solver on the second equation
00476    mcPOLYSOLVLOG(wxT("final solving of second equation")); 
00477    if (!math_SolveLineFirstDegree(equ2, unk, semisteps2))
00478     return FALSE;
00479    mcMATHLOG(wxT("\n\n%s\n\n"), mcTXT(semisteps2));
00480    
00481    // now, merge the two step arrays into the original one...
00482    steps.math_AppendCombinationOf(semisteps1, semisteps2, mcLO_OR);
00483    
00484    // completed !
00485    return TRUE;
00486   }
00487  }
00488 
00489  return TRUE;
00490 }
00491 
00492 
00493 


Documentation generated with Doxygen on Sun Feb 6 17:10:48 2005
Visit MathStudio home page for more info

[ Top ]