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 "Number.h" 00037 #endif 00038 00039 // Includes 00040 #include "mc/mcprec.h" 00041 #ifdef __BORLANDC__ 00042 #pragma hdrstop 00043 #endif 00044 00045 #ifndef mcPRECOMP 00046 #include <wx/dcscreen.h> 00047 #include "mc/MathCore.h" 00048 #include "mc/Number.h" 00049 #include "mc/Monomial.h" 00050 #include "mc/Fraction.h" 00051 #endif 00052 00053 00054 00055 mcIMPLEMENT_MAIN_CLASS(mcNumber, mcExpElement); 00056 00057 00058 // setup static variables 00059 mcNumber *mcNumberHelpers::smath_pOne = NULL; 00060 mcNumber *mcNumberHelpers::smath_pTwo = NULL; 00061 mcNumber *mcNumberHelpers::smath_pFour = NULL; 00062 mcNumber *mcNumberHelpers::smath_pMinusOne = NULL; 00063 mcNumber *mcNumberHelpers::smath_pZero = NULL; 00064 00065 wxString mcNumberHelpers::sgui_strFloatingPoint = wxT(".,"); 00066 int mcNumberHelpers::sgui_nDigitToShow = -1; 00067 bool mcNumberHelpers::smath_bUseIntegersWhenPossible = TRUE; 00068 00069 00070 // global objects 00071 mcNumber mcEmptyNumber(NULL); 00072 00073 00074 00075 // ---------------------------------------- 00076 // mcNUMBER 00077 // ---------------------------------------- 00078 00079 mcFraction mcNumber::math_TransformInFraction() 00080 { return hlp()->math_TransformInFraction(); } 00081 00082 00083 00084 00085 // ---------------------------------------- 00086 // mcNUMBERDATA 00087 // ---------------------------------------- 00088 00089 bool mcNumberHelpers::gui_isDigit(int c) { 00090 switch (c) { 00091 case wxT('0'): case wxT('1'): 00092 case wxT('2'): case wxT('3'): 00093 case wxT('4'): case wxT('5'): 00094 case wxT('6'): case wxT('7'): 00095 case wxT('8'): case wxT('9'): 00096 return TRUE; 00097 } 00098 return FALSE; 00099 } 00100 00101 /* 00102 void mcNumberHelpers::data_UpdateSign() 00103 { 00104 // sometimes, this routine is called when the element section is 00105 // not ready yet 00106 if (hlp() == NULL || hlp()->isBeingDeleted()) 00107 return; 00108 00109 mcRealValue n = Get(); 00110 00111 // SPECIAL BEHAVIOUR: always keep the values stored in mcNumbers 00112 // positive; just notify the sign change to our parent, if it is 00113 // a mcMonomial. 00114 // If this would not be done, then the signs of monomials 00115 // (stored in the form of mcAddOp/mcSubOps placed between each 00116 // mcMonomial contained in a mcPolynomial) would be useless 00117 // because they would not reflect their coefficient' signs. 00118 mcElement p = hlp()->GetParent(); 00119 if (p && p.data_GetType() == mcET_MONOMIAL && n < 0.0) { 00120 00121 // if we manage to change the sign of our parent, then 00122 // we can mantain the contents positive... 00123 if (((mcMonomial*)p).math_ChangeSign()) 00124 m_n = m_n.abs(); // don't use Set() because it calls us 00125 } 00126 } 00127 */ 00128 00129 00130 00131 00132 // ---------------------------------------- 00133 // mcNUMBERGUI 00134 // ---------------------------------------- 00135 00136 bool mcNumberHelpers::gui_isBeginKey(const mcKey &ev) const 00137 { 00138 if (mcNumberHelpers::gui_isDigit(ev.GetKeyCode()) && ev.GetModifiers() == 0) 00139 return TRUE; 00140 return FALSE; 00141 } 00142 00143 bool mcNumberHelpers::gui_isBaseEndKey(const mcKey &ev) const 00144 { 00145 // when cursor is inside num, legal inputs are digits, 00146 // delete & cancel keys and edit exponent key 00147 if (mcNumberHelpers::gui_isDigit(ev.GetKeyCode()) || 00148 mcNumberHelpers::gui_isDecimalPoint(ev.GetKeyCode()) || 00149 mcMathCore::Get()->MatchEditKeys(ev)) 00150 return FALSE; 00151 return TRUE; 00152 } 00153 00154 void mcNumberHelpers::gui_DoRecalcBaseSize() 00155 { 00156 wxString tmp = gui_GetStr(); 00157 00158 // using correct font, retrieve base size 00159 wxScreenDC dc; 00160 gui_SelectStyle(dc); 00161 mgui_szBase = gui_GetSizeOf(&dc, tmp); 00162 } 00163 00164 int mcNumberHelpers::gui_DrawBase(wxDC &hDC, int x, int y, long flags, 00165 const wxPoint &pt) const 00166 { 00167 mcGUILOG(wxT("mcNumberHelpers::gui_DrawBase")); 00168 wxString tmp = gui_GetStr(); 00169 00170 if (!(flags & mcDRW_NONACTIVE)) { 00171 00172 // if the given point is valid (that is, flags & mcDRW_USEPOINT == 1) 00173 // or flags & mcDRW_ALLACTIVE == 1, then we must draw ourselves as active 00174 hDC.SetBrush(*mcElementHelpers::sgui_pActivationBrush); 00175 hDC.SetPen(*wxBLACK_PEN); 00176 hDC.DrawRectangle(x-sgui_nAdditionalActivationSpaceLeftRight, y, 00177 gui_GetBaseSize().GetWidth()+sgui_nAdditionalActivationSpaceLeftRight*2, 00178 gui_GetBaseSize().GetHeight()); 00179 } 00180 00181 // print the base using correct font and colors 00182 gui_SelectStyle(hDC); 00183 hDC.SetBackgroundMode(wxTRANSPARENT); 00184 hDC.DrawText(tmp, x, y); 00185 00186 return data_GetID(); 00187 } 00188 00189 void mcNumberHelpers::gui_EditBase() 00190 { 00191 // cursor is now editing the base 00192 mgui_nCursorPos = data_Get().GetNumOfDigits(); 00193 mgui_nCursorLoc = mcECL_INSIDEBASE; 00194 } 00195 00196 void mcNumberHelpers::gui_Set(const wxString &str) 00197 { 00198 wxString n = str; 00199 00200 // first of all, remove the leading zeroes 00201 while (n.GetChar(0) == wxT('0')) { 00202 00203 // don't forget to move the cursor !!! 00204 n.Remove(0, 1); 00205 mgui_nCursorPos--; 00206 00207 // anyway, don't make the string empty: n == "0" is valid... 00208 if (n.Len() == 1) 00209 break; 00210 } 00211 00212 // reset & sync the number 00213 mgui_strTrailer = wxT(""); 00214 data_Set(n); 00215 00216 // do we have a trailer to memorize ? 00217 int len = gui_GetStr().Len(); 00218 if (len < (int)n.Len()) 00219 mgui_strTrailer = n.Right(n.Len()-len); 00220 } 00221 00222 mcInputRes mcNumberHelpers::gui_BaseInput(const mcKey &key, mcElement *pnew) 00223 { 00224 // handle the EDITEXP and EDITSUBSCRIPT keypresses 00225 if (gui_HandleSubExpEditKeys(key) == mcIR_OKAY) 00226 return mcIR_OKAY; 00227 00228 // do not base the input function on the cursor position 00229 if (mcMathCore::Get()->m_pDeleteKey->MatchKey(key)) { 00230 00231 if (mgui_nCursorPos == 0) { 00232 00233 // delete the previous element 00234 return mcIR_DELETE_PREVIOUS; 00235 } 00236 00237 if (mgui_nCursorPos == 1 && data_Get().GetNumOfDigits() == 1) { 00238 00239 // this element contains only one digit: delete it 00240 return mcIR_DELETE_THIS; 00241 } 00242 00243 // delete the digit which is placed on the left of the cursor 00244 wxString str = gui_GetStr(); 00245 str.Remove(mgui_nCursorPos-1, 1); 00246 gui_Set(str); 00247 00248 // move cursor 00249 mgui_nCursorPos--; 00250 00251 } else if (mcMathCore::Get()->m_pCancelKey->MatchKey(key)) { 00252 00253 if (mgui_nCursorPos == data_Get().GetNumOfDigits()) { 00254 00255 // element is placed on the rightmost point 00256 return mcIR_DELETE_NEXT; 00257 } 00258 00259 if (mgui_nCursorPos == 0 && data_Get().GetNumOfDigits() == 1) { 00260 00261 // this element contains only one digit: delete it 00262 return mcIR_DELETE_THIS; 00263 } 00264 00265 // delete the digit which is placed on the right of the cursor 00266 wxString str = gui_GetStr(); 00267 str.Remove(mgui_nCursorPos, 1); 00268 gui_Set(str); 00269 00270 // don't move the cursor 00271 00272 } else if (mcNumberHelpers::gui_isDigit(key.GetKeyCode())) { 00273 00274 // add the digit at the current pos of data_Get()... 00275 if (data_Get() == 0 && mgui_nCursorPos == 0) mgui_nCursorPos++; 00276 00277 // insert the digit in the number-like string 00278 wxString str = gui_GetStr(); 00279 str = str.Left(mgui_nCursorPos) + key.GetKeyCode() + 00280 str.Right(str.Len()-mgui_nCursorPos); 00281 mgui_nCursorPos++; 00282 00283 // then, convert the string to mcRealValue 00284 gui_Set(str); 00285 00286 } else if (mcNumberHelpers::gui_isDecimalPoint(key.GetKeyCode())) { 00287 00288 // check if this is possible... 00289 if (!data_Get().isInteger()) { 00290 mcMathCore::Get()->SyntaxError(wxT("Cannot insert another decimal point !!!")); 00291 return mcIR_OKAY; 00292 } 00293 00294 // then, add the decimal point... 00295 //data_Set(Get() + 0.5); 00296 mgui_strTrailer = mcNUMBER_DECIMAL_POINT; 00297 mgui_nCursorPos++; 00298 } 00299 00300 // recompute size 00301 gui_RecalcSize(); 00302 00303 return mcIR_OKAY; 00304 } 00305 00306 mcInsertRes mcNumberHelpers::gui_BaseInsert(const mcElement &toinsert, mcElement *newelem) 00307 { 00308 00309 00310 return mcINSR_OKAY; 00311 } 00312 00313 bool mcNumberHelpers::gui_Split(mcElement *p) 00314 { 00315 bool b = FALSE; 00316 00317 // the user pressed an end char while the cursor is still inside 00318 // the digits... this element must be split in two parts divided 00319 // by the operator or the element typed in. The first one will be 00320 // stored here, in this number, the second half, instead, must be 00321 // returned as a pointer to a new element 00322 mcNumber pnew; 00323 00324 // WARNING: the order of these two lines cannot be changed, because 00325 // first call depends from data_Get(), which is modified by second call 00326 pnew.data_Set(data_Get().GetFromDigitRange(mgui_nCursorPos, -1)); 00327 this->data_Set(data_Get().GetFromDigitRange(0, mgui_nCursorPos)); 00328 00329 pnew.gui_RecalcSize(); 00330 gui_RecalcSize(); 00331 00332 // if one of the two elements has the num set as zero, we must return NULL 00333 // for it, because cursor is at the begin/end of this element. 00334 if (this->data_Get() == 0) 00335 b = TRUE; // yes, this element must be removed 00336 00337 *p = pnew; 00338 if (pnew.data_Get() == 0) 00339 *p = NULL; // the second element is empty... 00340 00341 return b; 00342 } 00343 00344 bool mcNumberHelpers::gui_MergeWith(const mcElement &p) 00345 { 00346 if (p.data_GetType() != mcET_NUMBER) 00347 return FALSE; // could not perform merging... 00348 00349 mcNumber n(p); 00350 int len = n.data_Get().GetNumOfDigits(); 00351 00352 // merge the two numbers 00353 mcRealValue res = data_Get()*mcRealValue(10).pow(len) + n.data_Get(); 00354 data_Set(res); 00355 00356 // size should have changed 00357 gui_RecalcSize(); 00358 00359 // merge was okay 00360 return TRUE; 00361 } 00362 00363 mcMoveCursorRes mcNumberHelpers::gui_BaseMoveCursor(mcMoveCursorFlag flag, long modifiers) 00364 { 00365 int n; 00366 00367 // up/down movements (don't care about actual cursor position) 00368 if (flag == mcMCF_UP) 00369 return mcMCR_SETFOCUS_ABOVE; 00370 if (flag == mcMCF_DOWN) 00371 return mcMCR_SETFOCUS_BELOW; 00372 00373 switch (flag) { 00374 case mcMCF_LEFT: 00375 00376 if (mgui_nCursorPos > 0) { 00377 // cursor is somewhere inside num; we still 00378 // have space on the left 00379 mgui_nCursorPos--; 00380 00381 } else if (mgui_nCursorPos == 0) { 00382 // the cursor is at the left of the leftmost digit of 00383 // this element... moving cursor left, focus is not 00384 // our any more 00385 return mcMCR_SETFOCUS_PREVIOUS; 00386 } 00387 break; 00388 00389 case mcMCF_RIGHT: 00390 n = data_Get().GetNumOfDigits(); // how many chars is the base long ? 00391 00392 if (mgui_nCursorPos < n) { 00393 // cursor is somewhere inside num; we still 00394 // have space on the right 00395 mgui_nCursorPos++; 00396 00397 } else { 00398 00399 // the cursor is at the rightmost digit of this 00400 // element... moving cursor right, focus is not 00401 // our any more 00402 return mcMCR_SETFOCUS_NEXT; 00403 } 00404 break; 00405 00406 case mcMCF_UP: 00407 case mcMCF_DOWN: 00408 break; // already checked (just to avoid warnings) 00409 } 00410 00411 // everything was okay 00412 return mcMCR_OKAY; 00413 } 00414 00415 int mcNumberHelpers::gui_BaseMoveCursorUsingPoint(wxDC &hDC, const wxPoint &pt) 00416 { 00417 int w, h, r, n; 00418 wxRect rc; 00419 00420 // be sure given pointer is okay 00421 mcASSERT(pt.x > 0 && pt.y > 0, wxT("Invalid pointer")); 00422 00423 // find the nearest digit to the given poit 00424 w = gui_GetWidthOfChar(&hDC, this); 00425 h = gui_GetHeightOfChar(&hDC, this); 00426 r = data_Get().GetNumOfDigits(); 00427 00428 for (n=0; n < r; n++) { 00429 00430 // build the rectangle for the n-th digit of this number 00431 rc.x = n*w; 00432 rc.width = w; 00433 rc.y = 0; 00434 rc.height = h; 00435 00436 if (rc.Inside(pt)) { 00437 00438 // test if the cursor is in the left half of this digit 00439 rc.width /= 2; 00440 if (rc.Inside(pt)) 00441 mgui_nCursorPos = n; 00442 else 00443 mgui_nCursorPos = n+1; 00444 } 00445 } 00446 00447 return mcMCR_OKAY; 00448 } 00449 00450 int mcNumberHelpers::gui_GetBaseRelCursorPos(wxDC &hDC, wxPoint *pt) const 00451 { 00452 wxString tmp = gui_GetStr(); 00453 int n; 00454 00455 // convert in a string of chars the digits at the left of the cursor, 00456 // deleting all the digits on the right respect the cursor position 00457 tmp = tmp.Left(mgui_nCursorPos); 00458 00459 // set the cursor position next to the first mgui_nCursorPos 00460 // digits (in coord. relative to this element) of this element 00461 gui_SelectStyle(hDC); 00462 pt->x = gui_GetWidthOf(&hDC, tmp); 00463 pt->y = 0; 00464 00465 // if we are at the leftmost digit of this mcNumber, this 00466 // function will return the height of an empty string... 00467 // it's better check this special case 00468 n = ((tmp.Length() == 0) ? 00469 gui_GetHeightOfChar(&hDC, this) : 00470 gui_GetHeightOf(&hDC, tmp)); 00471 00472 return n; 00473 } 00474 00475 void mcNumberHelpers::gui_GetBaseCursorPos(mcCursorPos &cp) const 00476 { 00477 // if cursor is at the left of the leftmost digit, it's at the 00478 // beginning of this element 00479 if (mgui_nCursorPos == 0) { 00480 cp.gui_Push(mcCP_BEGIN); 00481 return; 00482 } 00483 00484 // if the cursor is at the right of the rightmost digit (and the 00485 // element hasn't got a visible exponent), cursor is at the end 00486 if (mgui_nCursorPos == (int)gui_GetStr().Len()) { 00487 cp.gui_Push(mcCP_END); 00488 return; 00489 } 00490 00491 // cursor must be somewhere inside the base 00492 cp.gui_Push(mgui_nCursorPos); 00493 } 00494 00495 void mcNumberHelpers::gui_SetBaseCursorPos(const mcCursorPos &cp) 00496 { 00497 if (cp.isEnd()) 00498 // if there's no exp, the rightmost digit of this mcNumber is 00499 // the rightmost digit of the base; otherwise set the cursor 00500 // position at "cnumber level" but at the end of the exp... 00501 mgui_nCursorPos = gui_GetStr().Len(); 00502 else if (cp.isBegin()) 00503 // mgui_nCursorPos=0 always means cursor is at the xmost 00504 // digit of the entire mcNumber 00505 mgui_nCursorPos = 0; 00506 } 00507 00508 00509 00510 00511 00512 00513 // ---------------------------------------- 00514 // mcNUMBERIO 00515 // ---------------------------------------- 00516 00517 bool mcNumberHelpers::io_isBeginChar(const wxString &str) const 00518 { 00519 if (str.IsNumber() && mcNumberHelpers::gui_isDigit(str.GetChar(0))) 00520 return TRUE; 00521 return FALSE; 00522 } 00523 00524 wxXml2Node mcNumberHelpers::io_GetBaseMathML(bool bGetPresentation) const 00525 { 00526 if (bGetPresentation) { 00527 00528 // create an <mn> tag 00529 return wxXml2Node(wxXML_TEXT_NODE, wxXml2EmptyDoc, wxT("mn"), 00530 data_Get().GetExtStr()); 00531 00532 } else { 00533 00534 // create a <cn> tag 00535 return wxXml2Node(wxXML_TEXT_NODE, wxXml2EmptyDoc, wxT("cn"), 00536 data_Get().GetExtStr()); 00537 } 00538 } 00539 00540 wxString mcNumberHelpers::io_GetBaseInlinedExpr() const 00541 { 00542 // just do a number2string conversion 00543 return data_Get().GetSmartStr(); 00544 } 00545 00546 bool mcNumberHelpers::io_ImportPresentationMathML(wxXml2Node tag, wxString &pErr) 00547 { 00548 mcASSERT(tag.GetName() == wxT("mn"), wxT("Error in mcNumberHelpers::io_isBeginTag()")); 00549 00550 if (tag.GetChildren().GetType() != wxXML_TEXT_NODE) { 00551 00552 // ooooooops 00553 pErr = wxT("The MathML to import is not valid MathML 2.0\n") 00554 wxT("or there was an error while parsing it."); 00555 return FALSE; 00556 } 00557 00558 // extract data & check it 00559 data_Set(tag.GetChildren().GetContent()); 00560 data_AddProperty(mcEP_INITIALIZED); 00561 00562 // everything should be perfect 00563 return TRUE; 00564 } 00565 00566 bool mcNumberHelpers::io_ImportBaseInlinedExpr(const wxString &str, int *count, wxString &pErr) 00567 { 00568 // extract data 00569 *count = 0; 00570 for (int i=0; i < (int)str.Len(); i++) { 00571 wxChar c = str.GetChar(i); 00572 if (mcNumberHelpers::gui_isDigit(c) || c == wxT('.') || c == wxT(',')) 00573 (*count)++; // advance number-lenght counter 00574 else 00575 break; // all the digits must be adjacent.... 00576 } 00577 00578 mcASSERT(*count > 0, wxT("There should be at least one digit...")); 00579 00580 // extract the actual info 00581 data_Set(str.Left(*count)); 00582 data_AddProperty(mcEP_INITIALIZED); 00583 00584 return TRUE; 00585 } 00586 00587 00588 00589 00590 00591 // ---------------------------------------- 00592 // mcNUMBERMATH - basic operations 00593 // ---------------------------------------- 00594 00595 bool mcNumberHelpers::math_CanBeAddedWith(const mcElement &p) const 00596 { 00597 if (p.data_GetType() == mcET_NUMBER) 00598 return TRUE; 00599 00600 // mcExpElement::math_MultiplyBy would fail because it assumes always a 00601 // mcExpElement and all other mcExpElement-derived classes already 00602 // implement the add() operation for mcNumber... 00603 return FALSE; 00604 } 00605 00606 bool mcNumberHelpers::math_CanBeMultWith(const mcElement &p) const 00607 { 00608 // if this number is zero then it can be multiplied by everything: 00609 // the result will always be zero. 00610 if (data_Get() == 0.0) 00611 return TRUE; 00612 return math_CanBeAddedWith(p); 00613 } 00614 00615 bool mcNumberHelpers::math_CanBeDivBy(const mcElement &p) const 00616 { 00617 if (math_isZero(p)) return FALSE; 00618 00619 // if this number is zero then it can be divided by everything: 00620 // the result will always be zero. 00621 if (data_Get() == 0.0) 00622 return TRUE; 00623 00624 // otherwise, a number can be divided only by another number... 00625 if (p.data_GetType() != mcET_NUMBER) 00626 return FALSE; 00627 00628 mcNumber n(p); 00629 if (math_isValid() && n.math_isValid()) { 00630 00631 // do we have to care about integers/floating-point ? 00632 // anyway if one of the two numbers is not integer, 00633 // then we must perform floating-point division 00634 // (maybe it will be transformed into an integer later 00635 // in a simplify step...) 00636 if (!mcNumberHelpers::smath_bUseIntegersWhenPossible || 00637 !this->data_Get().isInteger() || !n.data_Get().isInteger()) 00638 return TRUE; 00639 00640 // this number and its divisor are integers; to have the result 00641 // integer, this number can be divided only by 00642 // numbers containing us as a factor... 00643 mcIntegerValue us = mcIntegerValue(data_Get()); 00644 mcIntegerValue it = mcIntegerValue(n.data_Get()); 00645 bool areprime = us.isPrimeTo(it); 00646 mcMATHLOG(wxT("mcNumberHelpers::math_CanBeDivBy - checking if %s and %s are prime; gcd is %s, res is %d"), 00647 us.GetStr().c_str(), it.GetStr().c_str(), us.GCD(it).GetStr().c_str(), areprime); 00648 00649 if (areprime) 00650 return FALSE; // we cannot make result integer ! 00651 return TRUE; 00652 } 00653 00654 // one of the two numbers is not valid... 00655 return FALSE; 00656 } 00657 00658 mcBasicOpRes mcNumberHelpers::math_Add(const mcElement &p, mcElement *pp, bool add) 00659 { 00660 mcASSERT(p.data_GetType() == mcET_NUMBER, wxT("Error in #math_CanBeAddedWith")); 00661 mcNumber n(p); 00662 00663 if (add) 00664 data_Set(data_Get() + n.data_Get()); 00665 else 00666 data_Set(data_Get() - n.data_Get()); 00667 00668 mcLOG(wxT("mcNumberHelpers::math_Add - ADDING; res is %s"), mcTXTV(data_Get())); 00669 00670 return mcBOR_REMOVE_OPERAND; 00671 } 00672 00673 mcBasicOpRes mcNumberHelpers::math_MultiplyBaseOnlyBy(const mcElement &p, mcElement *pp) 00674 { 00675 // check for a simple case 00676 if (data_Get() == 0.0) 00677 return mcBOR_REMOVE_OPERAND; // multiplication performed: 0*anything = 0 00678 00679 // multiply 00680 mcNumber n(p); 00681 mcASSERT(p.data_GetType() == mcET_NUMBER, wxT("Error in #math_CanBeMultWith")); 00682 00683 // here we cannot have problems of approximation: 00684 // - if both *this and n are integer, the result will be an integer 00685 // - if one of *this or n is floating-point, the result will be a floating-point 00686 // which will be eventually converted in a fraction by math_Simplify(long flags) 00687 data_Set(data_Get() * n.data_Get()); 00688 00689 // transform this number in a fraction, if needed 00690 if (!data_Get().isInteger() && mcNumberHelpers::smath_bUseIntegersWhenPossible) { 00691 00692 mcIntegerValue n1, n2; 00693 math_GetNumDen(n1, n2); 00694 data_Set(n1); 00695 00696 mcNumber replacement(n2); 00697 (*pp) = replacement; 00698 00699 return mcBOR_REPLACE_OPERAND; 00700 } 00701 00702 return mcBOR_REMOVE_OPERAND; 00703 } 00704 00705 mcBasicOpRes mcNumberHelpers::math_DivideBaseOnlyBy(const mcElement &p, mcElement *pp) 00706 { 00707 // special case: this number contains a simple zero 00708 if (data_Get() == 0.0) 00709 return mcBOR_REMOVE_OPERAND; // division performed: 0/anything = 0 00710 00711 // divide 00712 mcNumber n(p); 00713 mcASSERT(!math_isZero(p), wxT("Cannot perform divisions by zero")); 00714 mcASSERT(p.data_GetType() == mcET_NUMBER, wxT("Error in #math_CanBeDivBy")); 00715 00716 mcRealValue res = data_Get() / n.data_Get(); 00717 00718 // transform this number in a fraction, if needed 00719 if (!res.isInteger() && mcNumberHelpers::smath_bUseIntegersWhenPossible) { 00720 00721 //mcIntegerValue n1, n2; 00722 //math_GetNumDen(n1, n2); 00723 mcRationalValue rat(data_Get(), n.data_Get()); 00724 rat.Canonicalize(); 00725 00726 // use the canonicalized num & den 00727 data_Set(rat.GetNum()); 00728 mcNumber replacement(rat.GetDen()); 00729 00730 mcMATHLOG(wxT("mcNumberHelpers::math_DivideBaseOnlyBy [%s] - dividying by [%s] ") 00731 wxT("trying to preserve this as a fraction (%s/%s)..."), mcTXTTHIS, 00732 mcTXT(p), data_Get().GetStr().c_str(), replacement.data_Get().GetStr().c_str()); 00733 00734 (*pp) = replacement; 00735 return mcBOR_REPLACE_OPERAND; 00736 00737 } else { 00738 00739 // if the result is integer or we don't care about the integers/floating-point, 00740 // we can directly set the result... 00741 data_Set(res); 00742 } 00743 00744 return mcBOR_REMOVE_OPERAND; 00745 } 00746 00747 mcBasicOpRes mcNumberHelpers::math_MultiplyBaseBy(const mcElement &p, mcElement *pp) 00748 { 00749 // special case 00750 if (data_Get() == 0.0) 00751 return mcBOR_REMOVE_OPERAND; // multiplication performed: 0*anything = 0 00752 00753 // wrap the given element into a mcNumber, only after checking 00754 // for the case above 00755 mcNumber n(p); 00756 00757 // multiply taking in count the exponents 00758 mcASSERT(p.data_GetType() == mcET_NUMBER, wxT("Error in #math_CanBeMultWith")); 00759 00760 // both the numbers (and their exponents) should be valuables 00761 mcRealValue n1 = this->math_Evaluate(); 00762 mcRealValue n2 = n.math_Evaluate(); 00763 mcASSERT(n1.isValid() && n2.isValid(), wxT("Error in #math_CanBeMultWith")); 00764 00765 // just perform a multiplication 00766 data_Set(n1 * n2); 00767 00768 // transform this number in a fraction, if needed 00769 if (!data_Get().isInteger() && mcNumberHelpers::smath_bUseIntegersWhenPossible) { 00770 00771 mcIntegerValue m1, m2; 00772 math_GetNumDen(m1, m2); 00773 data_Set(m1); 00774 00775 mcNumber replacement(m2); 00776 (*pp) = replacement; 00777 00778 return mcBOR_REPLACE_OPERAND; 00779 } 00780 00781 return mcBOR_REMOVE_OPERAND; 00782 } 00783 00784 00785 00786 00787 00788 // ----------------- 00789 // mcNUMBERMATH 00790 // ----------------- 00791 00792 bool mcNumberHelpers::math_GetNumDen(mcIntegerValue &num, mcIntegerValue &den) 00793 { 00794 // try to find the num & den of the generator fraction of this number 00795 mcRationalValue rat(data_Get()); 00796 00797 // be sure it's a normalized fraction (no common factors between num & den) 00798 rat.Canonicalize(); 00799 00800 num = rat.GetNum(); 00801 den = rat.GetDen(); 00802 00803 return TRUE; 00804 } 00805 00806 mcFraction mcNumberHelpers::math_TransformInFraction() 00807 { 00808 mcIntegerValue n, d; 00809 if (!math_GetNumDen(n, d)) 00810 return NULL; 00811 00812 // create the element which must be used to replace this number... 00813 mcFraction f; 00814 00815 mcNumber num(n); 00816 mcNumber den(d); 00817 00818 f.data_GetNum().math_WrapSimple(num); 00819 f.data_GetDen().math_WrapSimple(den); 00820 00821 return f; 00822 } 00823 00824 bool mcNumberHelpers::math_isZero(const mcElement &p) 00825 { 00826 if (p.data_GetType() == mcET_NUMBER && 00827 mcNumber(p).data_Get() == 0.0) 00828 return TRUE; 00829 return FALSE; 00830 } 00831 00832 bool mcNumberHelpers::math_CompareThisOnly(const mcElement &p, long flags) const 00833 { 00834 if (!mcElementHelpers::math_CompareThisOnly(p, flags)) 00835 return FALSE; 00836 00837 // now we are sure p is a mcNumber 00838 if (mcNumber(p).data_Get() == data_Get()) 00839 return TRUE; 00840 return FALSE; 00841 } 00842 00843 mcExpSimRes mcNumberHelpers::math_SimplifyBase(long flags, mcElement *pnew) 00844 { 00845 // if this number is not an integer, then transform it !!! 00846 if (!data_Get().isInteger() && mcNumberHelpers::smath_bUseIntegersWhenPossible) { 00847 00848 (*pnew) = math_TransformInFraction(); 00849 if ((*pnew) == NULL) 00850 return mcESR_DONE; 00851 mcASSERT((*pnew) != mcEmptyElement, wxT("Could not simplify....")); 00852 00853 return mcESR_REPLACE_THIS; 00854 } 00855 00856 // on the base only, no simplifications can be done... 00857 return mcESR_DONE; 00858 } 00859 00860 mcExpSimRes mcNumberHelpers::math_SimplifyBaseExp(long flags, mcElement *) 00861 { 00862 // simplify base with the exponent if we can make the 00863 // resultant number more easier to "use": 00864 // 00865 // 2^64 should not be simplified 00866 // (18446744073709551616 is much more cumbersome) 00867 // 2^3 should be simplified: 8 is shorter & easier to use 00868 // 00869 00870 // raise a temporary clone of this element 00871 mcNumber tmp(this); 00872 mcRealValue exp = tmp.data_GetExp().math_Evaluate(); 00873 tmp.data_Set(data_Get().pow(exp)); 00874 tmp.data_DestroyExpSub(TRUE); 00875 00876 // and remember how much long it was... 00877 mcRealValue lenght = tmp.math_GetTotalLenght(); 00878 //delete tmp; 00879 00880 // ... then compare that lenght with the current one... 00881 if (math_GetTotalLenght() > lenght) { 00882 00883 // ... and if the raised form is shorter, then raise *this 00884 mcRealValue exp = data_GetExp().math_Evaluate(); 00885 data_Set(data_Get().pow(exp)); 00886 data_DestroyExpSub(TRUE); 00887 00888 // this element still cannot be used in the next calculations: 00889 // if we return mcSER_DONE here, we would get these two steps: 00890 // 00891 // 3+2^2 ---. 7 00892 // 00893 // instead, we want to have: 00894 // 00895 // 3+2^2 ---. 3+4 -----. 7 00896 // 00897 return mcESR_NOTFINISHED; 00898 00899 } else { 00900 00902 // since the raised form is longer, we should consider 00903 // to perform something which is the inverse of the process 00904 // we tried above: we could try to detect if this number is 00905 // a perfect power of something: 00906 // since 18446744073709551616 is more cumbersome than 2^64, 00907 // we should be able to detect such a number and transform 00908 // it to the relative power... 00909 } 00910 00911 return mcESR_DONE; 00912 } 00913 00914 mcExpSimRes mcNumberHelpers::math_ExpandBase(long flags, mcElement *) 00915 { 00916 return mcESR_DONE; 00917 /*if ( 00918 00919 mcRealValue exp = tmp.data_GetExp().math_Evaluate(); 00920 Set(Get().pow(exp)); 00921 00922 return mcESR_NOTFINISHED;*/ 00923 } 00924 00925 mcBasicOpRes mcNumberHelpers::math_MakeReciprocal(mcElement *pp) 00926 { 00927 mcRealValue tmp = data_Get(); 00928 00929 mcFraction f; 00930 f.data_SetNum(*mcPolynomialHelpers::smath_pOne); 00931 f.data_GetDen().math_WrapSimple(this); 00932 00933 *pp = f; 00934 return mcBOR_REMOVE_OPERAND; 00935 } 00936 00937 void mcNumberHelpers::math_RaiseBaseTo(mcRealValue n) 00938 { 00939 data_Set(data_Get().pow(n)); 00940 } 00941 00942 void mcNumberHelpers::math_RaiseTo(const mcIntegerValue &n) 00943 { 00944 mcRealValue res = data_Get().pow(mcRealValue(n)); 00945 00946 mcMATHLOG(wxT("mcNumberHelpers::math_RaiseTo - %s is being raised to %s; result is %s"), 00947 data_Get().GetStr().c_str(), n.GetStr().c_str(), res.GetStr().c_str()); 00948 00949 data_Set(res); 00950 } 00951 00952 mcMonomial mcNumberHelpers::math_GetBaseLCM(const mcElement &p) const 00953 { 00954 // just multiply ourselves with the given element 00955 if (this->data_Get().isInteger() && mcNumber(p).data_Get().isInteger()) { 00956 00957 mcIntegerValue lcm = mcIntegerValue(data_Get()).LCM(mcIntegerValue(mcNumber(p).data_Get())); 00958 mcNumber res(lcm); 00959 return res; 00960 } 00961 00962 return mcExpElementHelpers::math_GetBaseLCM(p); 00963 } 00964 00965 mcMonomial mcNumberHelpers::math_GetGCD(const mcElement &p) const 00966 { 00967 // leave the following cases to mcExpElementHelpers: 00968 // 1) we have the same base (and eventually an exponent) and it's not zero 00969 // 2) the given element is not a mcNumber 00970 if (data_GetType() != p.data_GetType()) 00971 return mcExpElementHelpers::math_GetGCD(p); 00972 if (math_CompareThisOnly(p, FALSE) && 00973 data_Get() != 0) // if CompareThisOnly returns TRUE p and *this are identic 00974 return mcExpElementHelpers::math_GetGCD(p); 00975 00976 // anyway, we have specifically to handle the following case: 00977 // 1) the given element is a mcNumber and we have different bases 00978 // 2) we and the given element are mcNumber set to zero 00979 00980 // setup some variables... 00981 mcRealValue n1 = mcNumber(p).math_Evaluate(); 00982 mcRealValue n2 = this->math_Evaluate(); 00983 00984 // by definition GCD(0, 0) = 1 00985 // by definition GCD can be applied only on integer numbers 00986 if ((n1 == 0.0 && n2 == 0.0) || 00987 (!n1.isInteger() || !n2.isInteger())) 00988 return *mcMonomialHelpers::smath_pOne; // GCD is 1.0 for non-integer numbers 00989 00990 // build the result 00991 mcNumber gcd(mcIntegerValue(n1).GCD(mcIntegerValue(n2))); 00992 mcMATHLOG(wxT("mcNumberHelpers::math_GetBaseGCD - the gcd between [%s] and [%s] is [%s]"), 00993 mcTXTTHIS, mcTXT(p), mcTXT(gcd)); 00994 00995 // and return it 00996 mcMonomial res; 00997 res.data_AddElements(&gcd, 1); 00998 return res; 00999 }
[ Top ] |