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 // // 00031 00032 00033 00034 // optimization for GCC compiler 00035 #ifdef __GNUG__ 00036 #pragma implementation "Fraction.cpp" 00037 #endif 00038 00039 // includes 00040 #include "mc/mcprec.h" 00041 #ifdef __BORLANDC__ 00042 #pragma hdrstop 00043 #endif 00044 00045 #ifndef mcPRECOMP 00046 #include "mc/MathCore.h" 00047 #include "mc/Fraction.h" 00048 #include "mc/Monomial.h" 00049 #include "mc/Number.h" 00050 #include "mc/Bracket.h" 00051 #endif 00052 00053 00054 00055 mcIMPLEMENT_MAIN_CLASS(mcFraction, mcElement); 00056 00057 00058 00059 // setup customizable variables 00060 int mcFractionHelpers::sgui_nSpaceAboveBelow = 2; 00061 int mcFractionHelpers::sgui_nSpaceBetween = 1; 00062 int mcFractionHelpers::sgui_nAdditionalWidth = 4; 00063 int mcFractionHelpers::sgui_nAdditionalSpace = 8; 00064 00065 // This is the special key which must be used to create a new fraction 00066 // through the mcElementGUI input system. 00067 // This is not a simple CTRL+F combination because gui_isBeginKey() will 00068 // return TRUE only if the given key is set as a special key. 00069 mcKey *mcFractionHelpers::sgui_pNewFraction = NULL; 00070 00071 00072 00073 00074 00075 // ---------------------------------------- 00076 // mcFRACTIONDATA 00077 // ---------------------------------------- 00078 00079 void mcFractionHelpers::data_AddElements(bool bNum, mcElement *p, int num, int pos, 00080 bool bOverwrite, bool bForceCopy) 00081 { 00082 // just call the math_AddElements function of the num/den 00083 if (bNum) 00084 data_GetNum().data_AddElements(p, num, pos, bOverwrite, bForceCopy); 00085 else 00086 data_GetDen().data_AddElements(p, num, pos, bOverwrite, bForceCopy); 00087 } 00088 00089 #ifdef __MCDEBUG__ 00090 00091 wxString mcFractionHelpers::data_Debug(long flags) const 00092 { 00093 int step = mcMathCore::Get()->m_nIndentationStep; 00094 return wxString::Format( 00095 wxT("mcFraction [\n") 00096 wxT("---->>>>>>>>>>>>>>>>>> NUMERATOR <<<<<<<<<<<<<<<<----\n") 00097 wxT("%s") 00098 wxT("---->>>>>>>>>>>>>>>>> DENOMINATOR <<<<<<<<<<<<<<<----\n") 00099 wxT("%s") 00100 wxT("mcFraction ]"), 00101 data_GetNum().data_GetDebug(step, flags).c_str(), 00102 data_GetDen().data_GetDebug(step, flags).c_str()); 00103 } 00104 00105 void mcFractionHelpers::data_Check() const 00106 { 00107 // first of all, check our children (that is, num & den) 00108 mcElementHelpers::data_Check(); 00109 00110 // then, check they are not empty... 00111 // FIXME: is this check okay ? 00112 mcASSERT(data_GetNum().data_GetCount() > 0, wxT("An empty numerator ?")); 00113 mcASSERT(data_GetDen().data_GetCount() > 0, wxT("An empty denominator ?")); 00114 } 00115 00116 #endif 00117 00118 00119 00120 00121 // ---------------------------------------- 00122 // mcFRACTIONGUI 00123 // ---------------------------------------- 00124 00125 bool mcFractionHelpers::gui_isBeginKey(const mcKey &key) const 00126 { 00127 if (key.MatchKey(*sgui_pNewFraction)) 00128 return TRUE; 00129 return FALSE; 00130 } 00131 00132 bool mcFractionHelpers::gui_isEndKey(const mcKey &ev) const 00133 { 00134 switch (mgui_nCursorPos) { 00135 case mcFRACTION_LEFTMOST: 00136 case mcFRACTION_RIGHTMOST: 00137 if (mcMathCore::Get()->m_pDeleteKey->MatchKey(ev) || 00138 mcMathCore::Get()->m_pCancelKey->MatchKey(ev)) 00139 return FALSE; 00140 return TRUE; 00141 00142 case mcFRACTION_INSIDENUM: 00143 return data_GetNum().gui_isEndKey(ev); 00144 00145 case mcFRACTION_INSIDEDEN: 00146 return data_GetDen().gui_isEndKey(ev); 00147 } 00148 00149 mcASSERT(0, wxT("Un-handled cursor position")); 00150 return FALSE; 00151 } 00152 00153 int mcFractionHelpers::gui_GetYAnchor() const 00154 { 00155 return data_GetNum().gui_GetHeight()+ 00156 sgui_nSpaceAboveBelow+sgui_nSpaceBetween+ 00157 mcElementHelpers::gui_GetThickness(NULL)/2; 00158 } 00159 00160 void mcFractionHelpers::gui_DoRecalcSize() 00161 { 00162 // calculate size of the fraction 00163 mgui_sz.SetWidth(mcMAX(data_GetNum().gui_GetWidth(), 00164 data_GetDen().gui_GetWidth()) + 00165 sgui_nAdditionalWidth + sgui_nAdditionalSpace); 00166 00167 mgui_sz.SetHeight(data_GetNum().gui_GetHeight() + 00168 data_GetDen().gui_GetHeight() + 00169 sgui_nSpaceAboveBelow * 2 + 00170 sgui_nSpaceBetween*2 + 00171 mcElementHelpers::gui_GetThickness(NULL)); 00172 } 00173 00174 wxPoint mcFractionHelpers::gui_GetNumPos() const 00175 { 00176 // calculate the position of the numerator using coordinates relative to 00177 // this element. Center it horizontally 00178 return wxPoint((gui_GetWidth()-data_GetNum().gui_GetWidth())/2, sgui_nSpaceAboveBelow); 00179 } 00180 00181 wxPoint mcFractionHelpers::gui_GetDenPos() const 00182 { 00183 // calculate the position of the denominator using coordinates relative to 00184 // this element. Center it horizontally 00185 return wxPoint((gui_GetWidth()-data_GetDen().gui_GetWidth())/2, 00186 data_GetNum().gui_GetHeight() + sgui_nSpaceBetween*2 + 00187 mcElementHelpers::gui_GetThickness(NULL) + sgui_nSpaceAboveBelow); 00188 } 00189 00190 void mcFractionHelpers::gui_OnSelect(wxDC &dc, wxRect &rc) 00191 { 00192 bool existselection = FALSE; 00193 00194 // collect data 00195 wxRect num(gui_GetNumPos(), data_GetNum().gui_GetSize()); 00196 wxRect den(gui_GetDenPos(), data_GetDen().gui_GetSize()); 00197 00198 // TODO: theorically we should such a check to be sure 00199 // that only math-coherent selections can be possible: 00200 /*if (hlp()->GetParent().data_GetType() == mcET_POLYNOMIAL) { 00201 if (((mcElementArray*)(hlp()->GetParent())).gui_GetSelElemCount() > 0) 00202 existselection = TRUE; 00203 }*/ 00204 00205 // remove old selection 00206 // FIXME: gui_DeSelect() is recursive: so we could try to remove 00207 // it from non-mcElementArray to gain speed 00208 gui_DeSelect(); 00209 00210 // since we haven't got access to our parent, avoid it 00211 existselection = FALSE; 00212 00213 // check if we must call the gui_OnSelect() function of the numerator, 00214 // the gui_OnSelect() function of the denominator or both... 00215 bool n = num.Intersects(rc), d = den.Intersects(rc); 00216 00217 if ((n && d) || existselection) { 00218 00219 // the selection intersects both numerator & denominator: we cannot allow 00220 // a partial selection: we must select all the element 00221 gui_SelectAll(); 00222 00223 } else if (n) { 00224 00225 // the selection intersects just numnerator: check it and eventually 00226 // select the fraction (to let the parent know we contain at least 00227 // one element selected) 00228 data_GetNum().gui_OnSelect(dc, rc); 00229 00230 if (data_GetNum().gui_isSelected()) 00231 gui_Select(); 00232 00233 } else if (d) { 00234 00235 data_GetDen().gui_OnSelect(dc, rc); 00236 00237 if (data_GetDen().gui_isSelected()) 00238 gui_Select(); 00239 } 00240 } 00241 00242 int mcFractionHelpers::gui_Draw(wxDC &hDC, int x, int y, long flags, const wxPoint &pt) const 00243 { 00244 wxPoint n = gui_GetNumPos(), d = gui_GetDenPos(), ptn, ptd; 00245 wxRect num(n, data_GetNum().gui_GetSize()); 00246 wxRect den(d, data_GetDen().gui_GetSize()); 00247 int f=0, numid, denid; 00248 long fn=flags, fd=flags; 00249 00250 // offset the rects 00251 num.x += x; 00252 num.y += y; 00253 den.x += x; 00254 den.y += y; 00255 00256 // if both the numerator and the denominator are entirely selected, then 00257 // we must avoid that the num and the den draw their own selection rectangles: 00258 // the parent of this element will care about that !!! 00259 if (this->gui_isAllSelected()) { 00260 00261 fn &= ~mcDRW_ALLOW_TOTAL_SELECTION; 00262 fd &= ~mcDRW_ALLOW_TOTAL_SELECTION; 00263 00264 } else { 00265 00266 fn |= mcDRW_ALLOW_TOTAL_SELECTION; 00267 fd |= mcDRW_ALLOW_TOTAL_SELECTION; 00268 } 00269 00270 // draw the numerator and the denominator 00271 ptn = ptd = pt; 00272 if (flags & mcDRW_USEPOINT) { 00273 00274 // check the position of the cursor and then delete 00275 // unnecessary work: the cursor cannot be both over num & den !!! 00276 if (num.Inside(pt)) { 00277 00278 // denominator can be drawn as non active 00279 fd |= mcDRW_NONACTIVE; 00280 ptd = wxDefaultPosition; 00281 f = 1; // return numerator's ID 00282 00283 } else if (den.Inside(pt)) { 00284 00285 // numerator can be drawn as non active 00286 fn |= mcDRW_NONACTIVE; 00287 ptn = wxDefaultPosition; 00288 f = -1; // return denominator's ID 00289 00290 } else { 00291 00292 // the cursor is not inside the numerator nor the denominator: 00293 // it must be placed in the empty space of the fraction... 00294 f = 1000; 00295 ptn = ptd = wxDefaultPosition; 00296 fn |= mcDRW_NONACTIVE; 00297 fd |= mcDRW_NONACTIVE; 00298 } 00299 } 00300 00301 // draw num & den 00302 numid = data_GetNum().gui_Draw(hDC, num.x, num.y, fn, ptn); 00303 denid = data_GetDen().gui_Draw(hDC, den.x, den.y, fd, ptd); 00304 00305 // draw the fraction line 00306 y += sgui_nSpaceAboveBelow+data_GetNum().gui_GetHeight()+sgui_nSpaceBetween; 00307 00308 // draw the fraction line thick 00309 hDC.SetPen(*wxBLACK_PEN); 00310 int thickness = mcElementHelpers::gui_GetThickness(&hDC); 00311 for (int i=0; i < thickness; i++, y++) 00312 hDC.DrawLine(x+sgui_nAdditionalSpace/2, y, 00313 x+gui_GetWidth()-sgui_nAdditionalSpace/2, y); 00314 00315 // choose which ID must be returned 00316 switch (f) { 00317 case 0: 00318 return data_GetID(); 00319 case 1: 00320 return numid; 00321 case -1: 00322 return denid; 00323 } 00324 00325 // cursor was on empty space 00326 return mcDRW_NOACTIVEELEM; 00327 } 00328 00329 mcInputRes mcFractionHelpers::gui_Input(const mcKey &key, mcElement *pnew) 00330 { 00331 int res; 00332 00333 // if the fraction hasn't been initialized yet 00334 if (key.MatchKey(*sgui_pNewFraction) && 00335 !data_hasProperty(mcEP_INITIALIZED)) { 00336 00337 // fill numerator and denominator with empty boxes 00338 data_GetNum().gui_AddNewEmptyMonomial(); 00339 data_GetDen().gui_AddNewEmptyMonomial(); 00340 00341 data_AddProperty(mcEP_INITIALIZED); 00342 gui_RecalcSize(); 00343 00344 return mcIR_OKAY; 00345 } 00346 00347 switch (mgui_nCursorPos) { 00348 case mcFRACTION_LEFTMOST: 00349 // since isEndChar should reject all inputs when cursor 00350 // is in this position, this code should never be executed 00351 if (mcMathCore::Get()->m_pDeleteKey->MatchKey(key)) 00352 return mcIR_DELETE_PREVIOUS; 00353 if (mcMathCore::Get()->m_pCancelKey->MatchKey(key)) 00354 return mcIR_DELETE_THIS; 00355 mcASSERT(0, wxT("Code should never get here; check isEndChar() function")); 00356 00357 case mcFRACTION_RIGHTMOST: 00358 if (mcMathCore::Get()->m_pDeleteKey->MatchKey(key)) 00359 return mcIR_DELETE_THIS; 00360 if (mcMathCore::Get()->m_pCancelKey->MatchKey(key)) 00361 return mcIR_DELETE_NEXT; 00362 mcASSERT(0, wxT("Code should never get here; check isEndChar() function")); 00363 00364 case mcFRACTION_INSIDENUM: 00365 res = data_GetNum().gui_Input(key, pnew); 00366 00367 if (res == mcIR_DELETE_PREVIOUS || res == mcIR_DELETE_THIS) 00368 return mcIR_DELETE_THIS; 00369 00370 gui_RecalcSize(); 00371 break; 00372 00373 case mcFRACTION_INSIDEDEN: 00374 res = data_GetDen().gui_Input(key, pnew); 00375 00376 if (res == mcIR_DELETE_PREVIOUS || res == mcIR_DELETE_THIS) 00377 return mcIR_DELETE_THIS; 00378 00379 gui_RecalcSize(); 00380 break; 00381 } 00382 00383 return mcIR_OKAY; 00384 } 00385 00386 mcInsertRes mcFractionHelpers::gui_Insert(const mcElement &toinsert, mcElement *newelem) 00387 { 00388 switch (mgui_nCursorPos) { 00389 case mcFRACTION_INSIDENUM: 00390 data_GetNum().gui_Insert(toinsert, NULL); 00391 break; 00392 00393 case mcFRACTION_INSIDEDEN: 00394 data_GetDen().gui_Insert(toinsert, NULL); 00395 break; 00396 00397 default: 00398 mcASSERT(0, wxT("Invalid cursor position")); 00399 } 00400 00401 return mcINSR_OKAY; 00402 } 00403 00404 mcMoveCursorRes mcFractionHelpers::gui_MoveCursor(mcMoveCursorFlag flags, long modifiers) 00405 { 00406 int result; 00407 00408 switch (mgui_nCursorPos) { 00409 case mcFRACTION_LEFTMOST: 00410 if (flags == mcMCF_LEFT) { 00411 return mcMCR_SETFOCUS_PREVIOUS; 00412 } 00413 if (flags == mcMCF_RIGHT) { 00414 mgui_nCursorPos = mcFRACTION_INSIDENUM; 00415 data_GetNum().gui_SetCursorPos(mcCP_BEGIN); 00416 } 00417 if (flags == mcMCF_UP) 00418 return mcMCR_SETFOCUS_ABOVE; 00419 if (flags == mcMCF_DOWN) 00420 return mcMCR_SETFOCUS_BELOW; 00421 break; 00422 00423 case mcFRACTION_RIGHTMOST: 00424 if (flags == mcMCF_LEFT) { 00425 mgui_nCursorPos = mcFRACTION_INSIDENUM; 00426 } 00427 if (flags == mcMCF_RIGHT) { 00428 return mcMCR_SETFOCUS_NEXT; 00429 } 00430 if (flags == mcMCF_UP) 00431 return mcMCR_SETFOCUS_ABOVE; 00432 if (flags == mcMCF_DOWN) 00433 return mcMCR_SETFOCUS_BELOW; 00434 break; 00435 00436 case mcFRACTION_INSIDENUM: 00437 00438 // drop request to the numerator 00439 result = data_GetNum().gui_MoveCursor(flags, modifiers); 00440 00441 // check if cursor is still inside the expression 00442 if (result == mcMCR_SETFOCUS_PREVIOUS) { 00443 mgui_nCursorPos = mcFRACTION_LEFTMOST; 00444 } 00445 if (result == mcMCR_SETFOCUS_NEXT) { 00446 mgui_nCursorPos = mcFRACTION_RIGHTMOST; 00447 } 00448 if (result == mcMCR_SETFOCUS_ABOVE) { 00449 return mcMCR_SETFOCUS_ABOVE; 00450 } 00451 if (result == mcMCR_SETFOCUS_BELOW) { 00452 mgui_nCursorPos = mcFRACTION_INSIDEDEN; 00453 data_GetDen().gui_SetCursorPos(mcCP_BEGIN); 00454 } 00455 00456 break; 00457 00458 case mcFRACTION_INSIDEDEN: 00459 // drop request to the denominator 00460 result = data_GetDen().gui_MoveCursor(flags, modifiers); 00461 00462 // check if cursor is still inside the expression 00463 if (result == mcMCR_SETFOCUS_PREVIOUS) { 00464 mgui_nCursorPos = mcFRACTION_LEFTMOST; 00465 } 00466 if (result == mcMCR_SETFOCUS_NEXT) { 00467 mgui_nCursorPos = mcFRACTION_RIGHTMOST; 00468 } 00469 if (result == mcMCR_SETFOCUS_ABOVE) { 00470 mgui_nCursorPos = mcFRACTION_INSIDENUM; 00471 data_GetNum().gui_SetCursorPos(mcCP_BEGIN); 00472 } 00473 if (result == mcMCR_SETFOCUS_BELOW) { 00474 return mcMCR_SETFOCUS_BELOW; 00475 } 00476 00477 break; 00478 } 00479 00480 return mcMCR_OKAY; 00481 } 00482 00483 int mcFractionHelpers::gui_MoveCursorUsingPoint(wxDC &dc, const wxPoint &pt) 00484 { 00485 // just check if the given point is inside the num or the den... 00486 wxRect num(gui_GetNumPos(), data_GetNum().gui_GetSize()); 00487 wxRect den(gui_GetDenPos(), data_GetDen().gui_GetSize()); 00488 00489 if (num.Inside(pt)) { 00490 mgui_nCursorPos = mcFRACTION_INSIDENUM; 00491 return data_GetNum().gui_MoveCursorUsingPoint(dc, pt); 00492 } 00493 if (den.Inside(pt)) { 00494 mgui_nCursorPos = mcFRACTION_INSIDEDEN; 00495 return data_GetDen().gui_MoveCursorUsingPoint(dc, pt); 00496 } 00497 00498 return mcMCR_OKAY; 00499 } 00500 00501 int mcFractionHelpers::gui_GetRelCursorPos(wxDC &hDC, wxPoint *pt) const 00502 { 00503 int n; 00504 00505 switch (mgui_nCursorPos) { 00506 case mcFRACTION_LEFTMOST: 00507 pt->x = 0; 00508 pt->y = 0; 00509 return gui_GetSize().GetHeight(); 00510 00511 case mcFRACTION_RIGHTMOST: 00512 pt->x = gui_GetSize().GetWidth(); 00513 pt->y = 0; 00514 return gui_GetSize().GetHeight(); 00515 00516 case mcFRACTION_INSIDENUM: 00517 n = data_GetNum().gui_GetRelCursorPos(hDC, pt); 00518 pt->x += gui_GetNumPos().x; 00519 pt->y += gui_GetNumPos().y; 00520 return n; 00521 00522 case mcFRACTION_INSIDEDEN: 00523 n = data_GetDen().gui_GetRelCursorPos(hDC, pt); 00524 pt->x += gui_GetDenPos().x; 00525 pt->y += gui_GetDenPos().y; 00526 return n; 00527 } 00528 00529 return 0; 00530 } 00531 00532 void mcFractionHelpers::gui_SetCursorPos(const mcCursorPos &cp) 00533 { 00534 if (cp.isBegin()) 00535 mgui_nCursorPos = mcFRACTION_LEFTMOST; 00536 else if (cp.isEnd()) 00537 mgui_nCursorPos = mcFRACTION_RIGHTMOST; 00538 else 00539 mcASSERT(0, wxT("Cannot accept these flags")); 00540 } 00541 00542 void mcFractionHelpers::gui_GetCursorPos(mcCursorPos &cp) const 00543 { 00544 if (mgui_nCursorPos == mcFRACTION_LEFTMOST) 00545 cp.gui_Push(mcCP_BEGIN); 00546 else if (mgui_nCursorPos == mcFRACTION_RIGHTMOST) 00547 cp.gui_Push(mcCP_END); 00548 else 00549 cp.gui_Push(mgui_nCursorPos); 00550 } 00551 00552 mcElement mcFractionHelpers::gui_GetSelection() const 00553 { 00554 bool n = data_GetNum().gui_isSelected(), 00555 d = data_GetDen().gui_isSelected(); 00556 00557 if (n && d) { 00558 00559 // a little check 00560 mcASSERT(gui_isAllSelected(), wxT("If both num & den are selected, the element ") 00561 wxT("should be marked as completely selected")); 00562 00563 // return a copy of this fraction: it's entirely selected !!! 00564 mcElement *toreturn = new mcFraction(this); 00565 return *toreturn; //TOTEST 00566 00567 } else if (n) { 00568 00569 // return only the selected elements of the numerator 00570 return data_GetNum().gui_GetSelection(); 00571 00572 } 00573 00574 return data_GetDen().gui_GetSelection(); 00575 } 00576 00577 void mcFractionHelpers::gui_DeleteSelection() 00578 { 00579 mcElementHelpers::gui_DeleteSelection(); 00580 00581 // be sure that num & den are not removed completely... 00582 if (data_GetNum().data_GetCount() == 0) 00583 data_GetNum().gui_AddNewEmptyMonomial(); 00584 if (data_GetDen().data_GetCount() == 0) 00585 data_GetDen().gui_AddNewEmptyMonomial(); 00586 00587 gui_RecalcSize(); 00588 } 00589 00590 00591 00592 00593 00594 00595 00596 // ---------------------------------------- 00597 // mcFractionIO 00598 // ---------------------------------------- 00599 00600 wxXml2Node mcFractionHelpers::io_GetMathML(bool bGetPresentation) const 00601 { 00602 wxXml2Node maintag; 00603 00604 // add some mathml code 00605 if (bGetPresentation) { 00606 00607 maintag.CreateTemp(wxXML_ELEMENT_NODE, wxXml2EmptyDoc, wxT("mfrac")); 00608 00609 } else { 00610 00611 maintag.CreateTemp(wxXML_ELEMENT_NODE, wxXml2EmptyDoc, wxT("apply")); 00612 wxXml2Node divide(wxXML_ELEMENT_NODE, maintag, wxT("divide")); 00613 } 00614 00615 // get math ml both for numerator & denominator 00616 wxXml2Node num = data_GetNum().io_GetMathML(bGetPresentation), 00617 den = data_GetDen().io_GetMathML(bGetPresentation); 00618 maintag.AddChild(num); 00619 maintag.AddChild(den); 00620 00621 return maintag; 00622 } 00623 00624 bool mcFractionHelpers::io_CheckBracketNeed(const mcPolynomial &p, const wxString &exp) const 00625 { 00626 bool res = FALSE; 00627 00628 // we need to bracketize this polynomial if: 00629 // 00630 // 1) the polynomial contains more than one element 00631 res |= (p.data_GetCount() > 1); 00632 00633 mcMonomial m = mcMonomial(p.data_GetElemOfType(0, mcET_MONOMIAL)); 00634 if (m != mcEmptyElement && m.data_GetCount() > 0) { 00635 00636 // 2) the polynomial, contains one element which is not 00637 // a mcBracket and, as inlined expr, it is longer than 3 characters.. 00638 res |= (m.data_Get(0).data_GetType() != mcET_BRACKET && exp.Len() > 3); 00639 } 00640 00641 return res; 00642 } 00643 00644 wxString mcFractionHelpers::io_GetInlinedExpr() const 00645 { 00646 wxString strNum = data_GetNum().io_GetInlinedExpr(); 00647 wxString strDen = data_GetDen().io_GetInlinedExpr(); 00648 00649 // export everything as ()/() 00650 bool bracketizeNum = io_CheckBracketNeed(data_GetNum(), strNum); 00651 bool bracketizeDen = io_CheckBracketNeed(data_GetDen(), strDen); 00652 00653 if (bracketizeNum) strNum = wxT("(") + strNum + wxT(")"); 00654 if (bracketizeDen) strDen = wxT("(") + strDen + wxT(")"); 00655 00656 // add the final slash 00657 return strNum + wxT("/") + strDen; 00658 } 00659 00660 bool mcFractionHelpers::io_ImportPresentationMathML(wxXml2Node tag, wxString &pErr) 00661 { 00662 mcASSERT(tag.GetName() == wxT("mfrac"), wxT("Error in mcFractionHelpers::io_isBeginTag()")); 00663 00664 // the given tag should have exactly 2 children 00665 wxXml2Node num = tag.GetChildren(); 00666 wxXml2Node den = num.GetNext(); 00667 00668 if (den.GetNext() != wxXml2EmptyNode) { 00669 pErr = wxT("Found an <MFRAC> tag with more than two nested tags."); 00670 return FALSE; 00671 } 00672 00673 if (den == wxXml2EmptyNode || num == wxXml2EmptyNode) { 00674 pErr = wxT("Found an <MFRAC> tag with less than two nested tags."); 00675 return FALSE; 00676 } 00677 00678 if (num.GetName() != wxT("mrow")) 00679 num.Encapsulate(wxT("mrow")); 00680 if (den.GetName() != wxT("mrow")) 00681 den.Encapsulate(wxT("mrow")); 00682 num.SetNext(den); 00683 tag.SetChildren(num); 00684 00685 if (!data_GetNum().io_ImportPresentationMathML(num, pErr)) 00686 return FALSE; 00687 if (!data_GetDen().io_ImportPresentationMathML(den, pErr)) 00688 return FALSE; 00689 00690 return TRUE; 00691 } 00692 00693 bool mcFractionHelpers::io_ImportInlinedExpr(const wxString &str, int *count, wxString &pErr) 00694 { 00695 mcUNUSED(str); 00696 mcUNUSED(count); 00697 mcUNUSED(pErr); 00698 00699 // this is not an import error; this is a programming flaw !! 00700 mcASSERT(0, wxT("mcFractionIO MathML import functions should never be called ") 00701 wxT("because mcDivOp will import all the data for the '/' symbols ") 00702 wxT("which will be then converted to mcFraction later...")); 00703 return FALSE; 00704 } 00705 00706 void mcFractionHelpers::io_SetNum(const wxString &num) 00707 { 00708 wxString str; 00709 00710 // data_GetNum().Init(num); wouldn't work because 00711 // mcElementArray::io_isBeginChar always returns FALSE... 00712 data_GetNum().io_ImportInlinedExpr(num, NULL, str); 00713 } 00714 00715 void mcFractionHelpers::io_SetDen(const wxString &den) 00716 { 00717 wxString str; 00718 00719 // data_GetNum().Init(num); wouldn't work because 00720 // mcElementArray::io_isBeginChar always returns FALSE... 00721 data_GetDen().io_ImportInlinedExpr(den, NULL, str); 00722 } 00723 00724 00725 00726 00727 // ---------------------------------------- 00728 // mcFRACTIONMATH 00729 // ---------------------------------------- 00730 00731 void mcFractionHelpers::math_SetExp(const mcPolynomial &p) 00732 { 00733 // set exponent both in the num & den 00734 // NOTE: if we set the same exponent both in num & den, 00735 // the result is no exponent because they can be 00736 // immediately simplified. 00737 // However, this function can still be useful for 00738 // other algorithms like math_Expand(long flags) 00739 //data_GetNum().math_SetExp(p); 00740 //data_GetDen().math_SetExp(p); 00741 mcASSERT(0, wxT("")); 00742 } 00743 00744 bool mcFractionHelpers::math_Compare(const mcElement &p, long flags) const 00745 { 00746 if (p.data_GetType() != mcET_FRACTION) 00747 return FALSE; 00748 00749 // both num & den must be tested... 00750 mcFraction f(p); 00751 return data_GetNum().math_Compare(f.data_GetNum(), flags) && 00752 data_GetDen().math_Compare(f.data_GetDen(), flags); 00753 } 00754 00755 mcMathType mcFractionHelpers::math_GetMathType() const 00756 { 00757 mcMathType num = data_GetNum().math_GetMathType(); 00758 mcMathType den = data_GetDen().math_GetMathType(); 00759 00760 // by now, just multiply the two classifications... 00761 num.math_MultiplyBy(den); 00762 00763 // ...then, check if the denominator contains one or more unknowns: if it 00764 // does, then the global math data must be considered as RATIONAL... 00765 if (data_GetDen().math_ContainsUnknowns()) 00766 num.m_tMath1 = mcMTL1_RATIONAL; 00767 00768 return num; 00769 } 00770 00771 mcRealValue mcFractionHelpers::math_Evaluate() const 00772 { 00773 mcRealValue num = data_GetNum().math_Evaluate(); 00774 mcRealValue den = data_GetDen().math_Evaluate(); 00775 00776 // can we proceed ? 00777 if (!num.isValid() || 00778 !den.isValid()) 00779 return *mcRealValue::pNAN; 00780 00781 // just perform a division between num & den 00782 return num/den; 00783 } 00784 00785 void mcFractionHelpers::math_Flip() 00786 { 00787 // just swap den & num 00788 mcPolynomial tmp = data_GetNum(); 00789 data_GetNum() = data_GetDen(); 00790 data_GetDen() = tmp; 00791 } 00792 00793 00794 00795 00796 00797 // --------------------------------- 00798 // mcFRACTIONMATH basic operations 00799 // --------------------------------- 00800 00801 bool mcFractionHelpers::math_CanBeMultWith(const mcElement &p) const 00802 { return TRUE; } 00803 00804 bool mcFractionHelpers::math_CanBeAddedWith(const mcElement &p) const 00805 { return TRUE; } 00806 00807 bool mcFractionHelpers::math_CanBeDivBy(const mcElement &p) const 00808 { return TRUE; } 00809 00810 00811 //void mcFractionHelpers::math_AddOrmath_Subtract(const mcElement &p, bool add) 00812 mcBasicOpRes mcFractionHelpers::math_Add(const mcElement &p, mcElement *pp, bool add) 00813 { 00814 switch (p.data_GetType()) { 00815 case mcET_FRACTION: 00816 { 00817 mcFraction f(p); 00818 00819 // now, make common den between the two denominators... 00820 mcMonomial lcm(data_GetDen().math_GetLCM(f.data_GetDen())); 00821 mcPolynomial common; 00822 common.data_AddNewMonomial(lcm); 00823 00824 // multiply the num of this fraction by the common den 00825 // divided by denominator of this fraction 00826 mcPolynomial mult(common); 00827 mult.math_SimpleDivideBy(data_GetDen()); 00828 data_GetNum().math_SimpleMultiplyBy(mult); 00829 00830 mult.data_DeleteAll(); // mult can be reused now 00831 00832 // now, multiply the num of the other fraction by the common 00833 // den divided by the denominator of the other fraction 00834 mult = common; 00835 mult.math_SimpleDivideBy(f.data_GetDen()); 00836 mcPolynomial tmp(f.data_GetNum()); 00837 tmp.math_SimpleMultiplyBy(mult); 00838 00839 // add/sub "tmp" to the numerator... 00840 if (add) 00841 data_GetNum().math_SimpleAdd(tmp); 00842 else 00843 data_GetNum().math_SimpleSubtract(tmp); 00844 00845 // set the common den as the denominator of this fraction 00846 data_SetDen(common); 00847 } 00848 break; 00849 00850 case mcET_NUMBER: 00851 case mcET_SYMBOL: 00852 case mcET_MONOMIAL: 00853 case mcET_POLYNOMIAL: 00854 case mcET_RADICAL: 00855 case mcET_BRACKET: 00856 case mcET_FUNCTION: 00857 { 00858 // multiply a copy of the denominator by the symbol/number 00859 // that we are processing... 00860 mcPolynomial tmp = data_GetDen(); 00861 tmp.math_SimpleMultiplyBy(p); 00862 00863 // ... and add/subtract it to the numerator... 00864 data_GetNum().math_SimpleAdd(tmp, add); 00865 } 00866 break; 00867 00868 default: 00869 mcASSERT(0, wxT("Something is really wrong...")); 00870 break; 00871 } 00872 00873 return mcBOR_REMOVE_OPERAND; 00874 } 00875 00876 mcBasicOpRes mcFractionHelpers::math_MultiplyBy(const mcElement &p, mcElement *pp) 00877 { 00878 mcASSERT(p.math_isValidMath(), wxT("Invalid operand")); 00879 00880 if (p.data_GetType() == mcET_FRACTION) { 00881 00882 mcFraction f(p); 00883 00884 // just multiply num by num & den by den: 00885 // 00886 // a c a*c 00887 // --- * --- = ----- 00888 // b d b*d 00889 // 00890 data_GetNum().math_SimpleMultiplyBy(f.data_GetNum()); 00891 data_GetDen().math_SimpleMultiplyBy(f.data_GetDen()); 00892 00893 } else { 00894 00895 // just multiply the numerator 00896 // 00897 // a a*c 00898 // --- * c = ----- 00899 // b b 00900 // 00901 data_GetNum().math_SimpleMultiplyBy(p); 00902 } 00903 00904 return mcBOR_REMOVE_OPERAND; 00905 } 00906 00907 mcBasicOpRes mcFractionHelpers::math_DivideBy(const mcElement &p, mcElement *pp) 00908 { 00909 mcASSERT(p.math_isValidMath(), wxT("Invalid operand")); 00910 00911 if (p.data_GetType() == mcET_FRACTION) { 00912 00913 mcFraction f(p); 00914 00915 // flip the other fraction 00916 // 00917 // a c a c 00918 // --- : --- = --- * --- 00919 // b d b d 00920 // 00921 f.math_Flip(); 00922 f.data_Check(); 00923 *pp = f; 00924 00925 return mcBOR_REPLACE_OPERAND_AND_SET_MULTOP; 00926 00927 } else { 00928 00929 // just multiply the denominator by the given element: 00930 // 00931 // a a 00932 // --- : c = ----- 00933 // b b*c 00934 // 00935 data_GetDen().math_SimpleMultiplyBy(p); 00936 } 00937 00938 return mcBOR_REMOVE_OPERAND; 00939 } 00940 00941 00942 00943 00944 // --------------------------------------- 00945 // mcFRACTION - simplification/expansion 00946 // --------------------------------------- 00947 00948 mcExpSimRes mcFractionHelpers::math_Simplify(long flags, mcElement *newelem) 00949 { 00950 mcPolynomial &n = data_GetNum(), &d = data_GetDen(); 00951 00952 // STEP #1: simplify num & den 00953 mcExpSimRes n1 = n.math_Simplify(flags); 00954 mcExpSimRes n2 = d.math_Simplify(flags); 00955 00956 // if num or den is not completely simplified, wait next call... 00957 if (n1 != mcESR_DONE || n2 != mcESR_DONE) 00958 return mcESR_NOTFINISHED; 00959 00960 // STEP #1b: simplify a "sign" if possible 00961 if (n.math_isFirstMonomialNegative() && d.math_isFirstMonomialNegative()) { 00962 00963 // ok, changing the sign both to num & den we solve the problem... 00964 n.math_ChangeAllSigns(); 00965 d.math_ChangeAllSigns(); 00966 return mcESR_NOTFINISHED; 00967 } 00968 00969 if (n.math_isFirstMonomialNegative()) { 00970 00971 // the denominator signs are okay as they are... 00972 // we'll change our signs changing the sign 00973 // in front of the monomial which contains us 00974 n.math_ChangeAllSigns(); 00975 return mcESR_CHANGE_SIGN; 00976 } 00977 00978 00979 // STEP #2: try to remove denominator (if it's possible !) 00980 // check if the denominator just contains a simple "1" or a 00981 // simple "-1"... 00982 if (d.math_GetCount() == 1) { 00983 00984 mcRealValue value = d.math_Evaluate(); 00985 00986 // handle the wxT("-1") case, reporting it to the "+1" case 00987 if (value == -1.0) { 00988 n.math_ChangeAllSigns(); 00989 value = 1.0; 00990 } 00991 00992 if (value == 1.0) { 00993 00994 // pack the numerator inside a bracket 00995 mcBracket br; 00996 br.data_SetContent(n); 00997 00998 // denominator can be removed... this fraction 00999 // can be simplified to its numerator 01000 (*newelem) = br; 01001 01002 return mcESR_REPLACE_THIS; 01003 } 01004 } 01005 01006 // STEP #3: try to simplify numerator with denominator... 01007 if (n.math_isFactorized() && d.math_isFactorized()) 01008 return math_SimplifyFactors(flags, newelem); 01009 01010 // if the num or den is not factorized after it has been completely 01011 // simplified, we cannot do anything more... 01012 return mcESR_DONE; 01013 } 01014 01015 mcExpSimRes mcFractionHelpers::math_SimplifyFactors(long flags, mcElement *newelem) 01016 { 01017 mcPolynomial &n = data_GetNum(), &d = data_GetDen(); 01018 01019 mcUNUSED(newelem); 01020 01021 // both num & den contain one monomial only... 01022 // thus we contain something like: 01023 // 01024 // a (ax+b)(...) ax + b 01025 // --- or ------------ but not ---------- 01026 // b a*b*(...) ... 01027 // 01028 // In this case, we can try to apply a divop 01029 // between all the num & den elements 01030 mcMonomial &m1 = n.math_Get(0); 01031 mcMonomial &m2 = d.math_Get(0); 01032 01033 for (int i=0; i < m1.math_GetCount(); i++) { 01034 01035 mcElement &first = (mcElement &)m1.math_Get(i); 01036 for (int j=0; j < m2.math_GetCount(); j++) { 01037 01038 mcElement &second = (mcElement &)m2.math_Get(j); 01039 if (first.math_CanBeDivBy(second)) { 01040 01041 mcMATHLOG(wxT("mcFractionHelpers::math_SimplifyFactors - dividing ") 01042 wxT("[%s] by [%s]"), mcTXT(first), mcTXT(second)); 01043 01044 // divide the i-th numerator's element 01045 // by the j-th denominator's element 01046 mcElement replacement; 01047 mcBasicOpRes r = first.math_DivideBy(second, &replacement); 01048 01049 //.math_HandleBasicOpRes(r, replacement, j); 01050 switch(r) { 01051 case mcBOR_INVALID: 01052 mcASSERT(0, wxT("Something was wrong")); 01053 break; 01054 01055 case mcBOR_REPLACE_OPERAND: 01056 m2.data_AddElements(&replacement, 1, 01057 m2.math_MathToDataIdx(j), TRUE); 01058 m2.data_Check(); 01059 break; 01060 01061 case mcBOR_REMOVE_OPERAND: 01062 m2.math_Remove(j, FALSE); 01063 break; 01064 01065 default: 01066 mcASSERT(0, wxT("TODO")); 01067 } 01068 01069 return mcESR_NOTFINISHED; 01070 } 01071 } 01072 } 01073 01074 // we tried to divide all the factors of the numerator with 01075 // all the factors of the denominator without success: 01076 // this fraction should be reduced to the minimal terms. 01077 return mcESR_DONE; 01078 } 01079 01080 mcExpSimRes mcFractionHelpers::math_Expand(long flags, mcElement *newelem) 01081 { 01082 mcPolynomial &n = data_GetNum(), &d = data_GetDen(); 01083 mcExpSimRes n1 = n.math_Expand(flags); 01084 mcExpSimRes n2 = d.math_Expand(flags); 01085 01086 // if num or den is not completely simplified, wait next call... 01087 if (n1 != mcESR_DONE || n2 != mcESR_DONE) 01088 return mcESR_NOTFINISHED; 01089 01090 return mcESR_DONE; 01091 } 01092 01093 /* 01094 mcPolynomial &mcFractionHelpers::math_&math_GetFactors() const 01095 { 01096 // 01097 }*/ 01098 01099 mcMonomial mcFractionHelpers::math_GetLCM(const mcElement &p) const 01100 { 01101 mcFraction f(p); 01102 mcFraction res; 01103 01104 mcPolynomial num = data_GetNum().math_GetLCM(f.data_GetNum()); 01105 mcPolynomial den = data_GetDen().math_GetLCM(f.data_GetDen()); 01106 res.data_SetNum(num); 01107 res.data_SetDen(den); 01108 01109 return res; 01110 } 01111 01112 mcMonomial mcFractionHelpers::math_GetGCD(const mcElement &p) const 01113 { 01114 mcFraction f(p); 01115 mcFraction res; 01116 01117 mcPolynomial num = data_GetNum().math_GetGCD(f.data_GetNum()); 01118 mcPolynomial den = data_GetDen().math_GetGCD(f.data_GetDen()); 01119 res.data_SetNum(num); 01120 res.data_SetDen(den); 01121 01122 return res; 01123 } 01124 01125 mcBasicOpRes mcFractionHelpers::math_RaiseTo(const mcPolynomial &p) 01126 { 01127 data_GetNum().math_SimpleRaiseTo(p); 01128 data_GetDen().math_SimpleRaiseTo(p); 01129 01130 return mcBOR_REMOVE_OPERAND; 01131 } 01132 01133
[ Top ] |