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
[ Top ] |