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

Number.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 //                                                                   //
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 }


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

[ Top ]