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

Fraction.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 "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 


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

[ Top ]