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

ElementArray.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 "ElementArray.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 "mc/MathUtils.h"
00047  #include "mc/ElementArray.h"
00048  #include "mc/Polynomial.h"
00049  #include "mc/Monomial.h"
00050  #include "mc/EmptyBox.h"  // for mcElementArrayHelpers::gui_math_AddNewEmptyBox
00051  #include "mc/Number.h"
00052  #include "mc/Bracket.h"
00053  #include "mc/Fraction.h"
00054  #include "mc/Symbol.h"
00055  #include "mc/Radical.h"
00056 #endif
00057 
00058 
00059 mcIMPLEMENT_ABSTRACT_CLASS(mcElementArray, mcElement);
00060 
00061 
00062 #ifdef mcELEMENTARRAY_DRAW_DECORATIONS
00063 
00064  // MathCore resources (files which are merged with the program)
00065  #include "BeginTri.xpm"
00066  #include "EndTri.xpm"
00067 
00068  // setup customizable variables
00069  wxBitmap *mcElementArrayHelpers::gui_pBeginTriBmp = NULL;//LoadMaskForXPM(BeginTri_xpm, wxColour(255, 255, 255));
00070  wxBitmap *mcElementArrayHelpers::gui_pEndTriBmp = NULL;//LoadMaskForXPM(EndTri_xpm, wxColour(255, 255, 255));
00071 
00072 #endif
00073 
00074 
00075 
00076 // setup customizable variables
00077 wxPen *mcElementArrayHelpers::sgui_pSelectionPen = NULL;
00078 bool mcElementArrayHelpers::sgui_bDrawSelectionInverting = TRUE;
00079 
00080 
00081 // global objects
00082 mcArrayEntry mcEmptyArrayEntry(NULL, -1);
00083 
00084 
00085 
00086 
00087 // ---------------------------------------------------
00088 // mcARRAYENTRY
00089 // ---------------------------------------------------
00090 
00091 mcElement &mcArrayEntry::data_GetRef() const
00092 { return ((mcElementArrayHelpers *)mdata_arr)->data_GetRefFromEntry(this); }
00093 
00094 int mcArrayEntry::data_GetIdx() const
00095 { return mdata_arr->data_GetIdxFromEntry(this); }
00096 
00097 int mcArrayEntry::math_GetIdx() const
00098 { return mdata_arr->math_DataToMathIdx(data_GetIdx()); }
00099 
00100 
00101 
00102 
00103 
00104 // ---------------------------------------------------
00105 // mcFILTER
00106 // ---------------------------------------------------
00107 
00108 mcFilter::mcFilter(mcFilterType type, mcElementType t1, int count1) 
00109 {
00110  // init arrays
00111  for (int i=0; i < mcNUM_ELEMENT_TYPES; i++) {
00112   //m_bTypeAllowed[i] = FALSE;
00113   m_nMaxInstances[i] = 0;
00114  }
00115  
00116  // init filter type
00117  m_nType = type;
00118 
00119  // init first allowed element type
00120  SetMaxInstancesFor(t1, count1);
00121 }
00122 
00123 void mcFilter::SetMaxInstancesFor(mcElementType t, int count)
00124 {
00125  //m_bTypeAllowed[t] = TRUE;
00126  m_nMaxInstances[t] = count;
00127 }
00128 
00129 bool mcFilter::isElementTypeAllowed(mcElementType t, int num)
00130 {
00131  if (m_nMaxInstances[t] == mcFILTER_NO_INSTANCES_LIMIT || 
00132   m_nMaxInstances[t] > num)
00133   return TRUE;
00134  return FALSE;
00135 }
00136 
00137 
00138 
00139 
00140 
00141 // ---------------------------------------------------
00142 // mcELEMENTARRAYDATA
00143 // ---------------------------------------------------
00144 
00145 #ifdef __MCDEBUG__
00146 
00147 wxString mcElementArrayHelpers::data_Debug(long flags) const
00148 {
00149  wxString f = wxT(" ");
00150  if (mcMathCore::Get()->isGUIEnabled() && data_isUsingFilter())
00151    f = wxT(" (using filter) ");
00152  wxString tmp = data_GetDebugName() + f + wxT("[\n");
00153  
00154  // for each element/empty box, add informations
00155  int step = mcMathCore::Get()->m_nIndentationStep, max = 1;
00156  for (int i=0; i < data_GetCount(); i++) {
00157 
00158   // get debug info for the i-th element of the array
00159   f = data_Get(i).data_GetDebug(step, flags);
00160 
00161   max = mcMAX(((int)f.Len()), max);
00162   tmp += f;
00163  }
00164  
00165  // add trailer info
00166  max = mcMIN(40, max);
00167  tmp += /*wxString(wxT('-'), max) +*/ data_GetDebugName() + wxT(" ]\n");
00168  
00169  return tmp;
00170 }
00171 
00172 void mcElementArrayHelpers::data_Check() const
00173 {
00174  // most basic checks
00175  mcElementHelpers::data_Check();
00176 
00177  // scan all the array searching checking them
00178  for (int i=0; i < data_GetCount(); i++) {
00179 
00180   // check the relations with this array
00181   /*mcASSERT(data_Get(i).GetParent() == hlp(), wxT("Wrong parent pointer found"));*/
00182 
00183   // check that this array doesn't contain an array of this same type:
00184   // NESTING IS NOT ALLOWED !!!!!!!!
00185   mcASSERT((data_Get(i).data_GetType() != data_GetType()), wxT("Nested array found"));
00186  }
00187 }
00188 
00189 #endif  // __MCDEBUG__
00190 
00191 bool mcElementArrayHelpers::data_isSameAs(const mcElementHelpers *p) const
00192 {
00193  if (!mcElementHelpers::data_isSameAs(p))
00194   return FALSE;
00195 
00196  const mcElementArrayHelpers *e = (const mcElementArrayHelpers *)p;
00197  if (mdata_nElements != e->mdata_nElements) 
00198   return FALSE;
00199 
00200  // children have been already compared by mcElementHelpers::sata_isSameAs
00201 /* bool same = TRUE;
00202  for (int i=0; i < mdata_nElements; i++)
00203   same &= (mdata_pElemArray[i] == e->mdata_pElemArray[i]);*/
00204  //return same;
00205  return TRUE;
00206 }
00207 
00208 bool mcElementArrayHelpers::data_isWrappingOnly(mcElementType t) const
00209 {
00210  // check that we contains only an element of the given type:
00211  // math_isWrappingOnly does the same but checking *only* monomials...
00212  if (data_GetCount() > 1)
00213   return FALSE;
00214  if (data_Get(0).data_GetType() == t)
00215   return TRUE;
00216  return FALSE;
00217 }
00218 
00219 const mcElement &mcElementArrayHelpers::data_GetWrapped(mcElementType t) const
00220 {
00221  if (!data_isWrappingOnly(t))
00222   return mcEmptyElement;
00223  return data_Get(0);
00224 }
00225 
00226 void mcElementArrayHelpers::data_AddNewEmptyBox(int pos)
00227 {
00228  mcEmptyBox pnew;
00229  pnew.data_AddProperty(mcEP_INITIALIZED);
00230  data_AddElements(&pnew, 1, pos, TRUE);
00231 }
00232 
00233 void mcElementArrayHelpers::data_Delete(int entry)
00234 {
00235  data_CheckIndex(entry);
00236  
00237  // the element currently placed at "entry" will be unreferenced
00238  // and, maybe, deleted...
00239  data_SetAsEmptyEntry(entry);
00240 }
00241 
00242 bool mcElementArrayHelpers::data_Delete(const mcElement &elem)
00243 {
00244  int idx = data_GetIndexOf(elem);
00245  if (idx == -1) return FALSE;
00246 
00247  data_Delete(idx);
00248  data_MoveElemLeft(idx);
00249  return TRUE;
00250 }
00251 
00252 void mcElementArrayHelpers::data_DeleteLast(int num)
00253 {
00254  int count = data_GetCount();
00255  for (int i=0; i < num; i++)
00256   data_Delete(count-1-i);
00257  mdata_nElements -= num;
00258 }
00259 
00260 void mcElementArrayHelpers::data_DeleteFirst(int num)
00261 {
00262  for (int i=0; i < num; i++) {
00263   data_Delete(0);
00264   data_MoveElemLeft(0);
00265  }
00266 }
00267 
00268 void mcElementArrayHelpers::data_DeleteAllElemType(mcElementType t)
00269 {
00270  for (int i=0, max=data_GetNumOfElemType(t); i < max; i++) {
00271 
00272   // always ask the index of the first element of the given type
00273   // because we are removing them...
00274   int idx = data_GetElemIndexOfType(0, t);
00275   data_Delete(idx);
00276   data_MoveElemLeft(idx);
00277  }
00278 }
00279 
00280 mcElement mcElementArrayHelpers::data_Detach(int entry)
00281 {
00282  data_CheckIndex(entry);
00283 
00284  // remove from us as children settings its parent to NULL
00285 // if (isValidElem(entry) && data_Get(entry)->GetParent() == hlp())
00286 //  data_Get(entry)->SetParent(NULL);
00287  mcElement p = data_Get(entry);
00288 
00289  // this leaves an hole in the array
00290  data_SetAsEmptyEntry(entry);
00291 
00292  return p;
00293 }
00294 
00295 mcElement mcElementArrayHelpers::data_DetachLastElem(int num)
00296 {
00297  int count = data_GetCount();
00298  mcElement p = mcEmptyElement;
00299 
00300  for (int i=0; i < num; i++)
00301   p = data_Detach(count-1-i);
00302 
00303  // update the counter
00304  mdata_nElements -= num;
00305 
00306  return p;
00307 }
00308 
00309 bool mcElementArrayHelpers::data_ScanArray(bool (*func)(const mcElement &), bool odd) const
00310 {
00311  bool flag = TRUE;
00312 
00313  for (int i=(int)!odd; i < data_GetCount(); i++)
00314   flag &= (*func)(data_Get(i));
00315  return flag;
00316 }
00317 
00318 void mcElementArrayHelpers::data_CheckArrayLimit()
00319 {
00320  // check if array is large enough
00321  if (data_GetCount()+1 >= mdata_nUpperBound) {
00322 
00323   // enlarge mdata_pElemArray array
00324   mcEXTEND_ARRAY(mcElement, mdata_pElemArray, mdata_nUpperBound,
00325    mdata_nUpperBound+mcELEMENTARRAY_DEFAULT_ROOM);
00326 
00327   mdata_nUpperBound += mcELEMENTARRAY_DEFAULT_ROOM;
00328 
00329   // set as empty the new entries; don't use the SetAsEmptyEntry
00330   // function because the contents of the entries are dirty;
00331   // SetAsEmptyEntry function could think they are valid pointers
00332 //  for (int i=0; i < mcELEMENTARRAY_DEFAULT_ROOM; i++)
00333 //   mdata_pElemArray[mdata_nUpperBound-mcELEMENTARRAY_DEFAULT_ROOM+i] = mcELEMENTARRAY_EMPTYENTRY;
00334  }
00335 }
00336 
00337 void mcElementArrayHelpers::data_DeleteAll()
00338 {
00339  // check array is allocated
00340  if (mdata_pElemArray == NULL)
00341   return;
00342 
00343  // iterate in the array deallocating elements
00344  for (int i=0; i < mdata_nUpperBound; i++)
00345   data_Delete(i);  // Delete() will check if this is not an empty entry
00346 
00347  // reset variables
00348  mdata_nElements = 0;
00349 }
00350 
00351 mcElement mcElementArrayHelpers::data_DetachAll()
00352 {
00353  // check array is allocated
00354  if (mdata_pElemArray == NULL)
00355   return mcEmptyElement;
00356 
00357  // iterate in the array detaching elements
00358  mcElement p = mcEmptyElement;
00359  for (int i=0; i < mdata_nUpperBound; i++)
00360   p = data_Detach(i);
00361 
00362  // reset variables
00363  mdata_nElements = 0;
00364 
00365  return p;
00366 }
00367 
00368 void mcElementArrayHelpers::data_MoveElemRight(int start)
00369 {
00370  // mdata_pElemArray[start] will be shifted right with all the following elements
00371  data_CheckArrayLimit();  // we are adding a new element...
00372  for (int n=mdata_nElements; n > start; n--)
00373   mdata_pElemArray[n] = mdata_pElemArray[n-1];
00374 
00375  // one element has been added
00376  mdata_nElements++;
00377 
00378  // the first shifted entry is dirty....
00379  data_SetAsEmptyEntry(start);
00380 }
00381 
00382 void mcElementArrayHelpers::data_MoveElemLeft(int start)
00383 {
00384  // mdata_pElemArray[start] will be replaced by the element on his right and so on
00385  for (; start < data_GetCount()-1; start++)
00386   mdata_pElemArray[start] = mdata_pElemArray[start+1];
00387 
00388  // one element has gone
00389  mdata_nElements--;
00390 
00391  // last element position is dirty...
00392  data_SetAsEmptyEntry(data_GetCount());
00393 }
00394 
00395 int mcElementArrayHelpers::data_GetIndexOf(const mcElement &p) const
00396 {
00397  // scan all the array searching the given pointer
00398  for (int i=0; i < data_GetCount(); i++)
00399   if (data_Get(i) == p)
00400    return i; // found it
00401 
00402   // it's not an element stored in this mcElementArray
00403   return -1;
00404 }
00405 
00406 void mcElementArrayHelpers::data_Swap(int index1, int index2)
00407 {
00408  data_CheckIndex(index1);
00409  data_CheckIndex(index2);
00410 
00411  // swap the two elements
00412  mcElement tmp = data_Get(index1);
00413  mdata_pElemArray[index1] = data_Get(index2);
00414  mdata_pElemArray[index2] = tmp;
00415 }
00416 
00417 void mcElementArrayHelpers::data_UpdateNeighbor(int n)
00418 {
00419  // update neighbors of element n if necessary
00420  if (n >= 1 && data_isValidElem(n-1) && data_Get(n-1).data_hasProperty(mcEP_NOTIFY_NEIGHBOR_CHANGE))
00421   data_Get(n-1).hlp()->data_OnNeighborChange();
00422  if (n < data_GetCount()-1 && data_isValidElem(n+1) && data_Get(n+1).data_hasProperty(mcEP_NOTIFY_NEIGHBOR_CHANGE))
00423   data_Get(n+1).hlp()->data_OnNeighborChange();
00424 }
00425 
00426 void mcElementArrayHelpers::data_Merge(const mcElementArray &p, int n, bool bAddToEnd)
00427 {
00428  // add the elements of the given array at the end of _this_ array
00429  data_AddElements(p.data_GetArray(n), 
00430     p.data_GetCount()-n,
00431     (bAddToEnd ? -1 : 0)); // if we want to add the elements at the
00432           // end of the array, then we must use -1
00433 }
00434 
00435 mcElement &mcElementArrayHelpers::data_AddNewElement(mcElementType t, bool bOverwrite, int pos)
00436 {
00437  // create the new element using the global factory class....
00438  mcElement pnew = mcElementHelpers::data_NewElem(t);
00439 
00440  // and add it to this array
00441  int n = data_AddElements(&pnew, 1, pos, bOverwrite); 
00442  return data_Get(n);
00443 }
00444 
00445 void mcElementArrayHelpers::data_Set(int n, const mcElement &pnew)
00446 {
00447  data_CheckIndex(n);
00448  mdata_pElemArray[n] = pnew;
00449  //mdata_pElemArray[n].data_MakePrivateCopy();
00450 
00451  // now that the element has been stored in the array, we can
00452  // reset the parent...
00453  //
00454  // VERY IMPORTANT: this must be done only AFTER the element
00455  //                 has been stored in the array because some
00456  //                 functions, on parent change, immediately
00457  //                 check the parent and, for mcMonomial they
00458  //                 start a special routine which works on
00459  //                 mcMonomial's array which *must* contain them...
00460 // pnew.SetParent(hlp());
00461 
00462  // ...set the parent relations
00463 // pnew.SetAtSameLevelOf(hlp());
00464 
00465  // preserve filters...
00466  /*      data_UpdateFilterFor(pnew, GetFilter());    */
00467 
00468  // update neighbors if needed
00469 // data_UpdateNeighbor(n);
00470 
00471 #ifdef mcEP_NOTIFY_NEIGHBOR_CHANGE
00472  // update the element itself if needed; it has just been created, so its
00473  // neighbors are changed (before it was created the neighbors didn't exist...)
00474  if (pnew.data_hasProperty(mcEP_NOTIFY_NEIGHBOR_CHANGE))
00475   mdata_pElemArray[n].hlp()->data_OnNeighborChange();
00476 #endif
00477 }
00478 
00479 int mcElementArrayHelpers::data_AddElements(const mcElement *p, int num, int pos, 
00480           bool bOverwrite, bool bForceCopy)
00481 {
00482  mcElement pnew;
00483  int firstpos = -1;
00484 
00485  // -1 means: insert elements at the end of the array (please note that
00486  // it doesn't have sense to overwrite elements at the end of the array...)
00487  if (pos == -1)
00488   pos = data_GetCount();
00489 
00490  // for each element we must insert/overwrite
00491  for (int i=0; i < num; i++) {
00492 
00493   // extract the i-th element from the given array
00494   pnew = p[i];
00495   mcASSERT(data_isValidElem(pnew), wxT("Cannot add empty elements"));
00496   mcASSERT(pnew.data_GetType() != data_GetType(), wxT("Cannot nest arrays"));
00497 
00498   if (bForceCopy) {
00499 
00500    // copy the element if specified: this is not the preferred choice
00501    // since COW system should be handle sharing/unsharing...
00502    pnew = p[i].data_Clone();
00503    mcMATHLOG(wxT("mcElementArrayHelpers::data_AddElements - I was forced to copy [%s]"), mcTXT(pnew));
00504   }
00505 
00506   if (pos == data_GetCount()) {
00507 
00508    // check if there is enough space
00509    data_CheckArrayLimit();
00510 
00511    // and add it to the array; in this case the bOverwrite
00512    // flag is ignored because we are inserting items at the
00513    // end of the array: there are no elements to overwrite
00514    data_Set(mdata_nElements, pnew);
00515    if (firstpos == -1) firstpos = mdata_nElements;
00516    mdata_nElements++;
00517 
00518   } else {
00519 
00520    // check also if the position is okay
00521    data_CheckIndex(pos);
00522 
00523    // overwrite the previous elements or insert new elements
00524    // shifting them right...
00525    if (bOverwrite) {
00526     // do not shift the array; delete the element placed
00527     // in the entry where the new element must be placed
00528     data_Delete(pos);
00529    } else {
00530     // shift the array right to make room for this new element
00531     data_MoveElemRight(pos);
00532    }
00533 
00534    // store new element
00535    data_Set(pos, pnew);
00536    if (firstpos == -1) firstpos = pos;
00537   }
00538 
00539   // store next element in the next position
00540   pos++;
00541  }
00542 
00543  return firstpos;
00544 }
00545 
00546 
00547 
00548 
00549 #define SCAN_ARRAY_COUNTING(do_check)     \
00550  int n=0;           \
00551  for (int i=0; i < data_GetCount(); i++)    \
00552   if (do_check) n++; /* found a valid entry */ \
00553  return n;
00554 
00555 int mcElementArrayHelpers::data_GetNumOfElemType(mcElementType t) const
00556 {
00557  SCAN_ARRAY_COUNTING(data_Get(i).data_GetType() == t);
00558 }
00559 
00560 int mcElementArrayHelpers::data_GetOpCount() const
00561 {
00562  SCAN_ARRAY_COUNTING(data_isOp(i));
00563 }
00564 
00565 
00566 #define SCAN_ARRAY_SEARCHING(do_check)      \
00567  /* PLEASE NOTE THAT WE MUST ALWAYS START FROM -1: */ \
00568  /* IN THIS WAY THE PARAMETER n MUST BE EXPRESSED */  \
00569  /* AS ZERO-BASED */          \
00570  int occurrence=-1;          \
00571                \
00572  /* scan the array searching for an element of */  \
00573  /* type t; then check if it matches what we are */  \
00574  /* searching.... */          \
00575  for (int i=0; i < data_GetCount(); i++) {    \
00576   if (do_check) {          \
00577    occurrence++;         \
00578    if (n == occurrence)       \
00579     return i;         \
00580   }             \
00581  }              \
00582                \
00583  /* we couldn't find the required occurence */   \
00584  return -1;
00585 
00586 
00587 int mcElementArrayHelpers::data_GetElemIndexOfType(int n, mcElementType t) const
00588 {
00589  SCAN_ARRAY_SEARCHING(data_Get(i).data_GetType() == t);
00590 }
00591 
00592 int mcElementArrayHelpers::data_GetNonOpElemIndex(int n) const
00593 {
00594  SCAN_ARRAY_SEARCHING(!data_isOp(i));
00595 }
00596 
00597 int mcElementArrayHelpers::data_GetOpIndex(int n) const
00598 {
00599  SCAN_ARRAY_SEARCHING(data_isOp(i));
00600 }
00601 
00602 
00603 
00604 #define RETURN_ELEM_FROM_INDEX(idx_func, cast) \
00605  int i;          \
00606  if ((i=idx_func) != -1)      \
00607   return (cast)data_Get(i);    \
00608             \
00609  /* no elements of type t found */   \
00610  return (cast)mcEmptyElement;
00611 
00612 
00613 mcElement &mcElementArrayHelpers::data_GetElemOfType(int n, mcElementType t) const
00614 {
00615  RETURN_ELEM_FROM_INDEX(data_GetElemIndexOfType(n, t), mcElement &);
00616 }
00617 
00618 mcElement &mcElementArrayHelpers::data_GetNonOpElem(int n) const
00619 {
00620  RETURN_ELEM_FROM_INDEX(data_GetNonOpElemIndex(n), mcElement &);
00621 }
00622 
00623 mcOperator &mcElementArrayHelpers::data_GetOp(int n) const
00624 {
00625  RETURN_ELEM_FROM_INDEX(data_GetOpIndex(n), mcOperator &);
00626 }
00627 
00628 
00629 int mcElementArrayHelpers::data_QueueElemType(mcElementType t, int firstpos)
00630 {
00631  int curpos=firstpos+2;
00632 
00633  // scan the monomial starting at the entry firstpos: all the elements
00634  // before are assumed to be not of type t
00635  for (int i=firstpos; i < data_GetCount(); i++) {
00636 
00637   // searching for elements of type t
00638   if (data_Get(i).data_GetType() == t) {
00639 
00640    // check if the first element after the + or - op is a number
00641    if (data_Get(firstpos).data_GetType() != t) {
00642 
00643     // use commutative property inside this monomial:
00644     // a*b*c = c*b*a   (example with firstpos==1)
00645     // where c is an element of type t and a is not.
00646     data_Swap(firstpos, i);
00647 
00648     // in this monomial, after this function call, an
00649     // element of type t will be surely present in firstpos
00650     //bPresent = TRUE;
00651 
00652    } else if (i != firstpos) {
00653 
00654     // place this element in the firstpos after the last
00655     // element of type t and subtype subt
00656     if (i != curpos)
00657      data_Swap(curpos, i);
00658    }
00659   }
00660  }
00661 
00662  return curpos-firstpos;
00663 }
00664 
00665 void mcElementArrayHelpers::data_DeepCopy(const mcElementHelpers *p)
00666 {
00667  const mcElementArrayHelpers *e = (const mcElementArrayHelpers *)p;
00668  
00669  // destroy previous contents if presents...
00670  data_DeleteAll();
00671  
00672  // copy the objects inside the array
00673  if (e->mdata_pElemArray != NULL)
00674   data_AddElements(e->data_GetArray(), e->data_GetCount(), 0, TRUE);
00675 
00676  // copy filter pointer
00677  mdata_pFilter = e->mdata_pFilter;
00678 
00679  // then copy base class's data
00680  mcElementHelpers::data_DeepCopy(p);
00681 }
00682 
00683 
00684 
00685 void mcElementArrayHelpers::data_SetFilter(const mcFilter *p)
00686 {
00687  // add the filter property recursively...
00688  data_ChangeFilter(p);
00689 
00690  mcElement pthis(this);
00691  data_UpdateFilterFor(pthis, p);
00692 }
00693 
00694 void mcElementArrayHelpers::data_ChangeFilter(const mcFilter *p)
00695 {
00696  mcSAFE_DELETE(mdata_pFilter);
00697  
00698  if (p) {
00699   mdata_pFilter = new mcFilter();
00700   mdata_pFilter->DeepCopy(p);
00701  }
00702 }
00703 
00704 void mcElementArrayHelpers::data_UpdateFilterFor(mcElement &p, const mcFilter *f)
00705 {
00706  // mcElementArray-derived classes have a filter associated with them
00707  p.data_AddRecursiveProperty(mcET_MONOMIAL, mcEP_ELEMENTARRAY_FILTER, (void *)f);
00708  p.data_AddRecursiveProperty(mcET_POLYNOMIAL, mcEP_ELEMENTARRAY_FILTER, (void *)f); 
00709 }
00710 
00711 bool mcElementArrayHelpers::data_isElementAllowed(mcElementType type) const
00712 {
00713  // before proceeding with the allocation of the new element, check
00714  // if filter is enabled...
00715  if (mdata_pFilter) {
00716 
00717   // ...yes; filtering is enabled; now check if this element can
00718   // be created...
00719   if (!mdata_pFilter->isElementTypeAllowed(type, data_GetNumOfElemType(type)))
00720    return FALSE;  // ... no, it isn't
00721  }
00722 
00723  return TRUE;
00724 }
00725 
00726 mcElementType mcElementArrayHelpers::data_GetOpTypeBetween(int n1, int n2) const
00727 {
00728  data_CheckIndex(n1);
00729  data_CheckIndex(n2);
00730 
00731  // check arguments
00732  if (n1 > n2) mcSWAP(int, n1, n2);
00733  if (n1 == n2) return data_Get(n1).data_GetType();
00734 
00735  // now, check the index we calculated
00736  mcASSERT((n2 != -1) && (n1 != -1), 
00737   wxT("Invalid indexes"));
00738  mcASSERT(n2 > n1, 
00739   wxT("GetIndex function doesn't work because n2 > n1"));
00740  mcASSERT(n2-n1 <= 2, 
00741   wxT("Two non-op elements cannot be separed by more than one element..."));
00742 
00743  // if the two elements are adjacent, then a netral operator is 
00744  // considered to be present...
00745  if (n1+1 == n2)
00746   return math_GetNeutralOpType();
00747 
00748  mcElementType res = data_Get(n1+1).data_GetType();
00749  mcASSERT(mcOperatorHelpers::data_isOp(res), 
00750    wxT("Two non-ops are separed by a non-op element ???"));
00751 
00752  return res;
00753 }
00754 
00755 mcElementType mcElementArrayHelpers::data_GetOpTypePreceding(int n) const
00756 {
00757  // no preceding operator ?
00758  if (n == 0 || (n > 0 && !data_isOp(n-1)))
00759   return math_GetNeutralOpType();
00760 
00761  mcASSERT(data_isOp(n-1), wxT("Something wrong"));
00762  return data_Get(n-1).data_GetType();
00763 }
00764 
00765 // these two are not implemented in the header because in this way
00766 // we can avoid mcOperator-dependency in mcElementArray.h
00767 
00768 bool mcElementArrayHelpers::data_isOp(int entry) const
00769 {
00770  data_CheckIndex(entry);
00771  return mcOperatorHelpers::data_isOp(data_Get(entry).data_GetType());
00772 }
00773 
00774 void mcElementArrayHelpers::data_AddNewOp(mcElementType type, int pos)
00775 {
00776  mcElement pnew = mcElementHelpers::data_NewElem(type);
00777  data_AddElements(&pnew, 1, pos, TRUE);
00778 }
00779 
00780 void mcElementArrayHelpers::data_Move(int dn, int dk)
00781 {
00783 
00784  // some checks
00785  mcASSERT(!data_isOp(dn), wxT("Invalid data index"));
00786  //if (data_isOp(dk))
00787  bool bmoveop = FALSE;
00788  if (dn > 0) bmoveop = data_isOp(dn-1);
00789 
00790  // detach the element to move
00791  mcElement tomove = data_Detach(dn);
00792  data_MoveElemLeft(dn);
00793 
00794  // detach also the operator if required
00795  mcElement optomove = NULL;
00796  if (bmoveop) {
00797   optomove = data_Detach(dn-1);
00798   data_MoveElemLeft(dn-1);
00799  }
00800 
00801  // then, make space in the new position
00802  data_MoveElemRight(dk);
00803  if (bmoveop) data_MoveElemRight(dk);
00804 
00805  // now we have 1 (or 2 if bmoveop == TRUE) holes at dk
00806  if (bmoveop)
00807   data_Set(dk++, optomove);
00808  data_Set(dk, tomove);
00809 }
00810 
00811 int mcElementArrayHelpers::data_MoveAllElemType(mcElementType t, mcElementArray &m)
00812 {
00813  int i,max;
00814 
00815  for (i=0,max=data_GetNumOfElemType(t); i < max; i++) {
00816 
00817   // find the first element of the type t 
00818   int dataidx = data_GetElemIndexOfType(0, t);  
00819 
00820   // firstly move the element preceding the one
00821   // we just found, if it is a mcOperator-derived
00822   if (dataidx > 0 && data_isOp(dataidx-1)) {
00823    mcElement p = data_Detach(dataidx-1);
00824    data_MoveElemLeft(dataidx-1);
00825    m.data_AddElements(&p, 1, -1, TRUE);
00826 
00827    // now the element we found is placed at dataidx-1
00828    // (since its operator was moved)
00829    dataidx--;
00830   }
00831 
00832   // and detach it from us
00833   mcElement p = data_Detach(dataidx);
00834   data_MoveElemLeft(dataidx);
00835 
00836   // add to m this element (in the right position) without
00837   // copying (since we just detached it from *this)
00838   m.data_AddElements(&p, 1, -1, TRUE);
00839 
00840  }
00841 
00842  // max is the number of element types of type t which we moved
00843  return max;
00844 }
00845 
00846 
00847 
00848 
00849 
00850 
00851 
00852 
00853 // ---------------------------------------------------
00854 // mcELEMENTARRAYGUI - helper functions
00855 // ---------------------------------------------------
00856 
00857 int mcElementArrayHelpers::gui_GetSpaceBetween() const
00858 {
00859  // The CMONOMIAL_SPACE_BETWEEN value cannot be used directly
00860  // because it could be too high for nested exponent... we must
00861  // use the exponent depth to adjust it...
00862  return gui_GetSpaceBetweenRatio()/(gui_GetExpDepth()+1);
00863 }
00864 
00865 bool mcElementArrayHelpers::gui_isEndKey(const mcKey &ev) const
00866 {
00867  if (data_GetCount() > 0 && data_Get(mgui_nCursorPos).gui_isEndKey(ev))
00868   if (gui_isArrEndKey(ev))
00869    return TRUE;
00870   return FALSE;
00871 }
00872 /*
00873 void mcElementArrayHelpers::gui_DeepCopy(const mcElementGUI *p)
00874 {
00875  mcElementArrayGUI *d = (mcElementArrayGUI *)p;
00876 
00877  // don't copy pgui_SelectionPen: it's static !!!
00878 
00879  mgui_nCursorPos = d->mgui_nCursorPos;
00880  mcElementHelpers::sgui_DeepCopy(p);
00881 }*/
00882 
00883 int mcElementArrayHelpers::gui_AddNewElement(mcElementType type, const mcKey &key, int pos)
00884 {
00885  if (pos == -1) pos = data_GetCount();
00886 
00887  // create a new element of the required type
00888  mcElement fakearray = mcElementHelpers::data_NewElem(type);
00889 
00890  // exponent depth is in the previous call, so something like
00891  //           fakearray[0].SetAtSameLevelOf(this);
00892  // is not required...
00893 
00894  // send it its begin character
00895  fakearray.gui_Input(key, NULL);
00896 
00897  // add it to the array
00898  data_AddElements(&fakearray, 1, pos, TRUE);
00899 
00900  // 'pos' is the index of the new element
00901  return pos;
00902 }
00903 
00904 void mcElementArrayHelpers::gui_AddNewEmptyBox(int pos)
00905 { 
00906  mcKey fake(mcEmptyBoxHelpers::sgui_pNewEmptyBox->GetEvent(), TRUE);
00907  gui_AddNewElement(mcET_EMPTYBOX, fake, pos); 
00908 }
00909 
00910 void mcElementArrayHelpers::gui_DeleteNextOp()
00911 {
00912  if (mgui_nCursorPos < data_GetCount()-1 &&
00913   data_isOp(mgui_nCursorPos+1)) {
00914 
00915   data_Delete(mgui_nCursorPos+1);
00916   data_MoveElemLeft(mgui_nCursorPos+1);
00917  }
00918 }
00919 
00920 void mcElementArrayHelpers::gui_DeletePreviousOp()
00921 {
00922  if (mgui_nCursorPos > 0 &&
00923   data_isOp(mgui_nCursorPos-1)) {
00924   data_Delete(mgui_nCursorPos-1);
00925   data_MoveElemLeft(mgui_nCursorPos-1);
00926   mgui_nCursorPos--;
00927  }
00928 }
00929 
00930 wxPoint mcElementArrayHelpers::gui_GetOriginOfElem(int n, int cl) const
00931 {
00932  wxPoint p;
00933  if (cl == -1) cl = gui_GetCenterLine();
00934 
00935  // use previous width to set up the x-coord
00936  // or recompute it using the gui_GetSize function
00937  p.x = gui_CalcSizeOfFirst(n, cl).GetWidth();
00938 
00939  // set up element rect
00940  p.y = cl - data_Get(n).gui_GetYAnchor();
00941 
00942  return p;
00943 }
00944 
00945 wxSize mcElementArrayHelpers::gui_CalcSizeOfRange(int begin, int end, int cl) const
00946 {
00947  wxSize tmp, acc;
00948  int sb = gui_GetSpaceBetween();
00949  if (cl == -1) cl = gui_GetCenterLine();
00950 
00951  // Sum the width of the contained elements and operators
00952  // and find the tallest one
00953  for (int c=begin; c < end; c++) {
00954 
00955   // retrieve element size
00956   const mcElement &g = data_Get(c);
00957   tmp.SetWidth(g.gui_GetWidth());
00958   tmp.SetHeight(cl - g.gui_GetYAnchor()+ g.gui_GetHeight());
00959 
00960   // if this element is hidden, we must not count the space between;
00961   // anyway, add its size to the accumulator object
00962   acc.SetWidth(acc.GetWidth() + tmp.GetWidth() + sb);
00963   acc.SetHeight(mcMAX(acc.GetHeight(), tmp.GetHeight()));
00964  }
00965 
00966  // return calculated size
00967  return acc;
00968 }
00969 
00970 wxSize mcElementArrayHelpers::gui_CalcSizeOfFirst(int nElem, int cl) const
00971 {
00972  // use the CalcSizeOfRange() function: my policy is NEVER COPY-AND-PASTE.
00973  // it's much better to create a single function with many variables and
00974  // that reuses the code (without copying and pasting anything). Then,
00975  // functions with more intuitive names like this, can be defined to make
00976  // the class more usable.
00977  if (cl == -1) cl = gui_GetCenterLine();
00978  return gui_CalcSizeOfRange(0, nElem, cl);
00979 }
00980 
00981 wxRect mcElementArrayHelpers::gui_GetSelectionRect(wxDC &, int x, int y, int cl) const
00982 {
00983  int sb = gui_GetSpaceBetween();
00984  if (cl == -1) cl = gui_GetCenterLine();
00985 
00986  // find the lowest and the highest y in the selection
00987  int a=gui_GetSelStart(), b=gui_GetSelEnd();
00988 
00989  // no selected elements ?
00990  if (a == -1) return wxRect(0, 0, 0, 0);
00991 
00992  // calculate the size of the selected elements
00993  wxSize tmp = gui_CalcSizeOfRange(a, b, cl);
00994 
00995  // find the value which must be used as y-coord for the selection rectangle:
00996  // to do this, we must find the highest y anchor inside the selection...
00997  int miny = 0xFFFF;
00998  for (int i=a; i < b; i++)
00999   miny = mcMIN(miny, cl - data_Get(i).gui_GetYAnchor());
01000 
01001  // return a rectangle generated using the previously caculated size and
01002  // the size of the previous elements.
01003  wxRect ret(x+gui_CalcSizeOfFirst(a, cl).GetWidth()-sb/2, y+miny+1,
01004   tmp.GetWidth()+sb, tmp.GetHeight()-miny-1); // add an entire sb because from x coord
01005              // has been subtracted sb/2 ...
01006  return ret;
01007 }
01008 
01009 int mcElementArrayHelpers::gui_DrawSelectedElements(wxDC &hDC, int x, int y, 
01010            long flags, const wxPoint &pt, int cl) const
01011 {
01012  // get start & end of the selection
01013  int a=gui_GetSelStart(), b=gui_GetSelEnd();
01014 
01015  // draw only the range of the selected element using our extra-flexible
01016  // gui_Draw() function....
01017  return gui_ExDraw(hDC, x, y, flags, pt, cl, a, b, TRUE);
01018 }
01019 
01020 int mcElementArrayHelpers::gui_DrawSelection(wxDC &hDC, int x, int y, long flags, 
01021           const wxPoint &pt, int cl) const
01022 {
01023  wxPoint mypt(pt);
01024  int myflags = flags;
01025 
01026  int n = mcDRW_NOACTIVEELEM;
01027  wxBrush *pbrush = wxWHITE_BRUSH;
01028  bool ret = FALSE;
01029 
01030  // no center line ? compute it
01031  if (cl == -1) cl = gui_GetCenterLine();
01032 
01033  // calculate selection rectangle
01034  wxRect rc = gui_GetSelectionRect(hDC, x, y, cl);
01035 
01036  // no selected elements ?
01037  if (rc.width == 0 || rc.height == 0)
01038   return n;
01039 
01040  // if only one element is selected, we must check if it is completely
01041  // selected or not, before continuing
01042  bool bDrawRect = FALSE;
01043 
01044  // if this array is entirely selected and the parent allows us, then
01045  // draw the selection rectangle....
01046  bDrawRect = this->gui_isAllSelected() && (flags & mcDRW_ALLOW_TOTAL_SELECTION);
01047 
01048  // check for other possibilities only if we haven't still the certainty
01049  // that we have to draw the selection rectangle
01050  if (!bDrawRect) {
01051 
01052   // if this array contains just one selected element and it is entirely
01053   // selected, we can draw the selection rectangle....
01054   int nn = data_GetCount();
01055   int ns = gui_GetSelElemCount();
01056 
01057   // this is used, for example, when the selection is created inside a
01058   // mcExpContainer, like mcBracket... or when the selection is created
01059   // in an non-container element with an exponent...
01060   bool selectokay = TRUE;
01061   for (int i=0; i < ns; i++)
01062    selectokay &= gui_GetSelElem(i).gui_isAllSelected();
01063   bDrawRect |= (nn > ns && selectokay == TRUE);
01064  }
01065 
01066  if (bDrawRect) {
01067 
01068   // check if cursor is inside the bb of the entire selection;
01069   // if the cursor is inside the entire selection...
01070   if (flags & mcDRW_ALLACTIVE || (flags & mcDRW_USEPOINT && rc.Inside(pt))) {
01071 
01072    // ...draw the selection rect with a different background color
01073    pbrush = mcElementHelpers::sgui_pSelectionBrush;
01074 
01075    // ...and set up everything as if we are drawing a selection which
01076    // do not contain any container element...
01077    // (if the selection contains one or more container element,
01078    // maybe only a portion of the container element is selected;
01079    // in this case, we couldn't say here that the selection must
01080    // be drawn entirely as non-active and that the mouse cursor
01081    // is placed on selection...)
01082    mypt = wxDefaultPosition;
01083    myflags = myflags & ~mcDRW_USEPOINT;
01084 
01085    n = mcDRW_ONSELECTION;
01086   }
01087 
01088   // draw the rectangle around selected elements (this means that
01089   // the selection doesn't contain any container element, or that
01090   // the selected elements are *entirely* selected; that is, if
01091   // a container element is inside the selection, all its elements
01092   // are selected)
01093   if (!sgui_bDrawSelectionInverting) {
01094    hDC.SetPen(*sgui_pSelectionPen);
01095    hDC.SetBrush(*pbrush);
01096    hDC.DrawRectangle(rc);
01097   }
01098 
01099   // remove from the flags given to the gui_DrawSelectedElements
01100   // function the mcDRW_ALLOW_TOTAL_SELECTION flag: we already
01101   // drawn the selection rectangle for all our contained elements
01102   myflags = myflags & ~mcDRW_ALLOW_TOTAL_SELECTION;
01103 
01104   // the selected elements will be drawn with the mcDRW_NONACTIVE
01105   // flag, so they would sure return the mcDRW_NOACTIVELEM code;
01106   // but, we checked before if the cursor is inside the draw
01107   // rectangle; in this case, in fact, we must return the
01108   // mcDRW_ONSELECTION code...
01109   ret = FALSE;
01110 
01111  } else {
01112 
01113   // the selection contains one or more container elements which
01114   // are not completely selected: this means that they will care
01115   // about the drawing of their selection rectangles and that
01116   // the mouse cursor maybe placed out of one of them...
01117   mypt = pt;
01118 
01119   // ...this, in turn, implies that we must return the ID of the
01120   // container element which is drawn with the selected elements...
01121   ret = TRUE;
01122  }
01123 
01124  // draw selected elements
01125  int n2 = gui_DrawSelectedElements(hDC, x, y, myflags, mypt, cl);
01126 
01127  // draw the two decorations
01128 #ifdef mcELEMENTARRAY_DRAW_DECORATIONS
01129 
01130  hDC.gui_DrawBitmap(*mcMathCore::Get()->bmpBeginTri, rc.x, rc.y, TRUE);
01131  hDC.gui_DrawBitmap(*mcMathCore::Get()->bmpEndTri, rc.x+rc.width-
01132   mcMathCore::Get()->bmpEndTri.gui_GetWidth()-1, rc.y+rc.height-
01133   mcMathCore::Get()->bmpEndTri.gui_GetHeight()-1, TRUE);
01134 
01135 #endif
01136 
01137  // now that we've drawn the selected elements we can draw the selection
01138  // inverting the colors, if the relative customizable is set so...
01139  if (bDrawRect && sgui_bDrawSelectionInverting) {
01140 
01141   mcGUILOG(wxT("mcElementArrayHelpers::gui_DrawSelection [%s] - ")
01142    wxT("drawing the selection rectangle INVERTING"), mcTXTTHIS);
01143 
01144   //hDC.SetBrush(*wxBLACK_BRUSH);
01145   //hDC.SetPen(*wxBLACK_PEN);//TRANSPARENT_PEN);
01146 
01147   // to invert the colors, we must use the logical function wxINVERT;
01148   // anyway we *must not* forget to restore old logical function !!
01149   int old = hDC.GetLogicalFunction();
01150   hDC.SetLogicalFunction(wxINVERT);
01151   hDC.DrawRectangle(rc);
01152   hDC.SetLogicalFunction(old);
01153  }
01154 
01155  // be sure to return mcDRW_ONSELECTION/mcDRW_NOACTIVEELEM, if the selection
01156  // does not  contain any container element or, otherwise, to return
01157  // the code that the container element has returned....
01158  return (ret == TRUE) ? n2 : n;
01159 }
01160 
01161 int mcElementArrayHelpers::gui_GetBB(int n, wxRect *rc, int yCenter, int w) const
01162 {
01163  mcUNUSED(w);
01164  
01165  // do we have to recompute all values ?
01166  if (yCenter == -1) yCenter = gui_GetCenterLine();
01167 
01168  wxPoint o = gui_GetOriginOfElem(n, yCenter);
01169  rc->x = o.x;
01170  rc->y = o.y;
01171  rc->height = data_Get(n).gui_GetHeight();
01172  rc->width = data_Get(n).gui_GetWidth();
01173 
01174  // add the space to leave between each pair of elements
01175  return rc->width + gui_GetSpaceBetween();
01176 }
01177 
01178 int mcElementArrayHelpers::gui_GetCenterLine() const
01179 {
01180  int y=0;
01181 
01182  // use the highest Y anchor value as center line
01183  for (int i=0; i < data_GetCount(); i++)
01184   y = mcMAX(y, data_Get(i).gui_GetYAnchor());
01185  return y;
01186 }
01187 
01188 void mcElementArrayHelpers::gui_DeleteSelection()
01189 {
01190  int i = gui_GetSelStart(), max = gui_GetSelEnd();
01191  if (max == -1) return;  // no selection...
01192  
01193  if (max-i == 1) {  // just one selected element ?
01194   
01195   // remove the elements selected inside it...
01196   mcElement m = gui_GetSelElem(0);
01197 
01198   if (m.gui_isAllSelected()) {
01199 
01200    // remove the element...
01201    data_Delete(i);
01202    data_MoveElemLeft(i);
01203 
01204   } else {
01205 
01206    // remove the selected portion of this element
01207    m.gui_DeleteSelection();
01208   }
01209   
01210  } else {
01211   
01212   // extended selection: remove all the selected monomials
01213   // (they should be entirely selected...)
01214   int todel = i;
01215   for (; i < max; i++) {
01216    mcASSERT(data_Get(todel).gui_isAllSelected(), 
01217     wxT("extended selection implies elements completely selected..."));
01218    data_Delete(todel);
01219    data_MoveElemLeft(todel);
01220   }
01221  }
01222  
01223  gui_RecalcSize();
01224 }
01225 
01226 
01227 
01228 
01229 // ---------------------------------------------------
01230 // mcELEMENTARRAYGUI - mcElementGUI implementations
01231 // ---------------------------------------------------
01232 
01233 void mcElementArrayHelpers::gui_DoRecalcSize()
01234 {
01235  // just use the CalcSizeOfFirst array function with all
01236  // the elements in the array....
01237  mgui_sz = gui_CalcSizeOfFirst(data_GetCount(), -1);
01238 }
01239 
01240 void mcElementArrayHelpers::gui_UpdateExpDepth()
01241 {
01242  // ElementArrays must apply changes in exponent level to all
01243  // the elements they contain....
01244  for (int i=0; i < data_GetCount(); i++)
01245   data_Get(i).gui_SetAtSameLevelOf(this);
01246 
01247  // this is NOT REQUIRED since the #gui_SetAtSameLevelOf
01248  // function automatically calls the #gui_UpdateExpDepth function
01249  mcElementHelpers::gui_UpdateExpDepth();
01250 }
01251 
01252 int mcElementArrayHelpers::gui_ExDraw(wxDC &dc, int x, int y, long flags, const wxPoint &pt, 
01253          int cl, int b, int e, bool bDrawSelected) const
01254 {
01255  mcGUILOG(wxT("mcElementArrayHelpers::gui_ExDraw [%s]"), mcTXTTHIS);
01256 
01257  int n=mcDRW_NOACTIVEELEM, w=0;
01258  wxRect rc;
01259 
01260  // find the highest y value for the center line
01261  if (cl == -1) cl = gui_GetCenterLine();
01262 
01263  // check the upper bound
01264  if (e == -1) e = data_GetCount();
01265 
01266  if (b > 0) {
01267 
01268   // if required, add the size of the skipped elements
01269   w = gui_CalcSizeOfFirst(b, cl).GetWidth();
01270  }
01271 
01272  // decide now the flags to give to all non active elements
01273  // we are going to draw...
01274  int nonactiveflags = mcDRW_NONACTIVE;
01275  if (flags & mcDRW_ALLOW_TOTAL_SELECTION)
01276   nonactiveflags |= mcDRW_ALLOW_TOTAL_SELECTION;
01277 
01278  // draw all the factors of this monomial
01279  for (int i=b; i < e; i++) {
01280 
01281   // get the element bounding box for the i-th element
01282   w += gui_GetBB(i, &rc, cl, w);
01283 
01284   // offset rect to get correct coord.
01285   rc.Offset(x, y);
01286 
01287   // is it selected ?
01288   if (data_Get(i).gui_isSelected()) {
01289 
01290    if (!bDrawSelected) {
01291 
01292     // skip this element: selected elements must then be drawn with
01293     // the gui_DrawSelection() function; this function should be
01294     // called by the derived class, if it wants. See the
01295     // differences between mcPolynomial::gui_Draw & mcMonomial::gui_Draw
01296     continue;
01297    }
01298   }
01299 
01300   // draw this element
01301   if (flags & mcDRW_ALLACTIVE || (flags & mcDRW_USEPOINT && rc.Inside(pt))) {
01302 
01303    // draw the element with the correct flag
01304    n = data_Get(i).gui_Draw(dc, rc.x, rc.y, flags, pt);
01305    mcGUILOG(wxT("mcElementArrayHelpers::gui_ExDraw - just drawn as active [%s] at (%d;%d)"), 
01306       mcTXT(data_Get(i)), rc.x, rc.y);
01307 
01308    // we can stop checking for cursor position; it is inside the
01309    // current element; the following elements cannot contain it
01310    if (!(flags & mcDRW_ALLACTIVE)) flags &= ~mcDRW_USEPOINT;
01311 
01312   } else {
01313 
01314    // the cursor is not inside this subelement
01315    data_Get(i).gui_Draw(dc, rc.x, rc.y, nonactiveflags, wxDefaultPosition);
01316    mcGUILOG(wxT("mcElementArrayHelpers::gui_ExDraw - just drawn as inactive [%s] at (%d;%d)"), 
01317       mcTXT(data_Get(i)), rc.x, rc.y);
01318   }
01319  }
01320 
01321  // a piece of code useful to debug...
01322 #ifdef mcELEMENTARRAY_DBG_DRAWCENTERLINE
01323  dc.DrawLine(x, y+cl, x+gui_GetWidth(), y+cl);
01324 #endif
01325 
01326  // return the ID of the active element
01327  return n;
01328 }
01329 
01330 int mcElementArrayHelpers::gui_ExGetRelCursorPos(wxDC &hDC, wxPoint *pt, int cl) const
01331 {
01332  int n, n2, n3 = ((cl == -1) ? gui_GetCenterLine() : cl);
01333 
01334  // be sure array is not empty and cursor position is okay
01335  if (data_GetCount() == 0)
01336   return 0;
01337  gui_CheckCursorPos();
01338 
01339  // Get relative position of the element where cursor is located
01340  n = data_Get(mgui_nCursorPos).gui_GetRelCursorPos(hDC, pt);
01341  n2 = data_Get(mgui_nCursorPos).gui_GetYAnchor();
01342 
01343  // offset the pt coord. to the coord. relative to this element
01344  // get the size of all the elements that precedes the cursor
01345  pt->x += gui_CalcSizeOfFirst(mgui_nCursorPos, cl).GetWidth();
01346 
01347  // offset the cursor of the same amount of pixel that was used
01348  // to offset the current focused element
01349  pt->y += n3-n2;
01350 
01351  return n;
01352 }
01353 
01354 void mcElementArrayHelpers::gui_ExOnSelect(wxDC &dc, wxRect &rc, int cl)
01355 {
01356  int i, w=0, begin = 0xFFFF, end = -1;
01357  wxRect bb;
01358 
01359 #if 0
01360  dc.SetPen(*wxBLACK_PEN);
01361  dc.SetBrush(*wxTRANSPARENT_BRUSH);
01362  dc.DrawRectangle(rc);
01363 #endif
01364 
01365  // no center line ? compute it
01366  if (cl == -1) cl = gui_GetCenterLine();
01367 
01368  // a new selection is in progress; delete old one
01369  gui_DeSelect();
01370 
01371  // we must call the element with the given rect intersecting their bounding boxes
01372  for (i=0; i < data_GetCount(); i++) {
01373 
01374   // retrieve element's bounding box and offset it to the current position
01375   w += gui_GetBB(i, &bb, cl, w);
01376 
01377   if (bb.Intersects(rc)) {
01378 
01379    // call the element function; but continue to test for
01380    // other elements which intersect the selection rectangle
01381    wxRect tmp(rc);
01382    tmp.Offset(-bb.x, -bb.y);
01383 
01384    data_Get(i).gui_OnSelect(dc, tmp);
01385    begin = mcMIN(begin, i);
01386    end = mcMAX(end, i+1);
01387 
01388   } /*else if (end != -1) {
01389 
01390    // we have already found the piece of the array which intersects
01391    // the selection rectangle... stop
01392 
01393    // WARNING: this cannot be done because some elements in the
01394    // middle of the array maybe too small to intersect selection; but
01395    // this doesn't mean that all the following are are so small, too.
01396   }*/
01397  }
01398 
01399 
01400  // ADJUST THE SELECTION IF IT IS EXTENDED AMONG VARIOUS ELEMENTS
01401  // (that is, selection is extended among 2 or more subelements)
01402  if (end-begin > 1) {
01403 
01404   // select all those elements whose bounding boxes are placed between the
01405   // begin and the end of the selected elements, but which do not intersect with
01406   // the selection rectangle because too small...
01407   for (i=begin; i < end; i++)
01408    data_Get(i).gui_SelectAll();
01409 
01410   // be sure also the operator placed before the first selected element (if present),
01411   // is selected, too. This is an important check because math functions
01412   // require it !!!
01413   int a = gui_GetSelStart();
01414   if (a > 0 && data_isOp(a-1)) // -1 means: there are no selected elements
01415    data_Get(a-1).gui_SelectAll();
01416 
01417   // be also sure that selection does not end with an operator; this is
01418   // another important prerequisite for math functions !!
01419   a = gui_GetSelEnd();
01420   if (a > 0 && data_isOp(a-1)) // a is the index of the first non-selected element
01421    data_Get(a-1).gui_DeSelect();
01422  }
01423 
01424  // if this element contains at least one selected element, we must set
01425  // the selection flag...
01426  // NOTE: we cannot just check if wxT("end") is different from "-1" because
01427  //       even if the selection rectangle intersects the bounding box
01428  //       of some of our childrens, they could have set themselves
01429  //       as non-selected since the selection rectangle intersects
01430  //       their _boundingbox_ not their graphical output
01431  if (gui_GetSelElemCount() > 0)
01432   this->gui_Select();
01433 }
01434 
01435 mcInputRes mcElementArrayHelpers::gui_Input(const mcKey &key, mcElement *pnew)
01436 {
01437  mcGUILOG(wxT("mcElementArrayHelpers::gui_Input - before processing: [%s]"), mcTXTTHIS);
01438 
01439  mcInputRes res = mcIR_OKAY;
01440  mcElementType n;
01441  mcCursorPos pos;
01442 
01443  // if a new element must be allocated...
01444  if (data_GetCount() == 0 || data_Get(mgui_nCursorPos).gui_isEndKey(key)) {
01445 
01446   // is k the begin char of a mcElement derived class ?
01447   n = mcElementHelpers::gui_isKeyBeginKey(key);
01448   if (n == mcET_INVALID) {
01449    
01450    // stop here key processing
01451    mcMathCore::Get()->SyntaxError(wxT("Character not recognized"));
01452    return mcIR_OKAY;
01453   }
01454 
01455   // check if the filter allow us to create an instance of this object...
01456   if (!data_isElementAllowed(n)) {
01457    
01458    // creation of this element is not allowed...
01459    mcMathCore::Get()->SyntaxError(wxT("Element not allowed"));
01460    return mcIR_OKAY;
01461   }
01462 
01463   // check where is the cursor at this moment
01464   if (data_GetCount() == 0)
01465    pos = mcCP_BEGIN;
01466   else
01467    pos = data_Get(mgui_nCursorPos).gui_GetCursorPos();
01468 
01469   /*switch (pos) {
01470   case mcCP_BEGIN:*/
01471   if (pos.isBegin()) {
01472 
01473    // we must shift the array to make room for a new
01474    // element located not at the end of the array
01475    // (here, we are sure ew are not at the end of the array
01476    // because we are at the beginning of an element);
01477 
01478    // move everything right to make space for the new element
01479    data_MoveElemRight(mgui_nCursorPos);
01480 
01481    // add the new element
01482    mgui_nCursorPos = gui_AddNewElement(n, key, mgui_nCursorPos);   
01483 
01484   } else if (pos.isInside()) {
01485 
01486    // the user has pressed an end char for the focused element
01487    // while the cursor was *inside* that element: we must then
01488    // split that element in two parts...
01489    // Typical examples of elements which can be split are
01490    // mcNumber and mcMonomial...
01491    gui_DoSplit(n, key);
01492 
01493   } else if (pos.isEnd()) {
01494 
01495    data_MoveElemRight(mgui_nCursorPos+1);
01496    //data_mdata_nElements--;
01497    mgui_nCursorPos = gui_AddNewElement(n, key, mgui_nCursorPos+1);
01498    //data_mdata_nElements++;
01499 
01500   } else {
01501 
01502    mcASSERT(0, wxT("Unhandled cursor position"));
01503   }
01504 
01505  } else {
01506 
01507   // deliver this input to the focused monomial
01508   res = data_Get(mgui_nCursorPos).gui_Input(key, pnew);
01509 
01510   // call the function which must "backprocess" the
01511   // returned flag (contained in 'n')
01512   res = gui_BackInput(key, pnew, res);
01513  }
01514 
01515  // size should have changed
01516  gui_UpdateExpDepth();  // this calls RecalcSize()
01517  //gui_DeepRecalcSize();
01518  gui_RecalcSize();
01519 
01520  // do some checks
01521  data_Check();
01522  gui_CheckCursorPos();
01523 
01524  // mcIR_OKAY by default, or something else if something has modified 'res'
01525  mcGUILOG(wxT("mcElementArrayHelpers::gui_Input - after processing: [%s]"), mcTXTTHIS);
01526  return res;
01527 }
01528 
01529 void mcElementArrayHelpers::gui_MoveCursorLeft()
01530 {
01531  for (int i=mgui_nCursorPos-1; i >= 0; i--) {
01532   if (data_Get(i).gui_LetInCursor(mcCP_END)) {
01533    mgui_nCursorPos = i;
01534    return;
01535   }
01536  }
01537 }
01538 
01539 void mcElementArrayHelpers::gui_MoveCursorRight()
01540 {
01541  for (int i=mgui_nCursorPos+1; i <= data_GetCount(); i++) {
01542   if (data_Get(i).gui_LetInCursor(mcCP_BEGIN)) {
01543    mgui_nCursorPos = i;
01544    return;
01545   }
01546  }
01547 }
01548 
01549 mcMoveCursorRes mcElementArrayHelpers::gui_MoveCursor(mcMoveCursorFlag flag, long modifiers)
01550 {
01551  int result=0;
01552 
01553  // check cursor position just to be sure mgui_nCursorPos is okay
01554  gui_CheckCursorPos();
01555 
01556  // call the move cursor function of the currently focused element
01557  result = data_Get(mgui_nCursorPos).gui_MoveCursor(flag, modifiers);
01558  if (data_Get(mgui_nCursorPos).gui_isSelected())
01559   gui_Select();
01560 
01561  // WARNING: this is experimental !!!
01562  // this code will be executed when the cursor is at the border
01563  // of two non-mcOperator elements:
01564  //
01565  //       a|x      123ax|(...)       23*56/7a|x
01566  //
01567  // This piece of code, in fact, will try to keep the cursor *always*
01568  // on the left element, in these cases... this is due to the fact
01569  // that, in this cases:
01570  // 
01571  //        123a|          [LEFT]
01572  //        123|a          [4]
01573  //
01574  // something like wxT("123*4a") would be created instead of "1234a" because
01575  // the cursor would be considered to be on the leftmost position of
01576  // the mcSymbol "a" instead of the rightmost position of the mcNumber
01577  // "123"... to avoid this, we need to keep the cursor always on the
01578  // left element...
01579  if (result == mcMCR_OKAY && 
01580   data_Get(mgui_nCursorPos).gui_GetCursorPos().isBegin()) {
01581   
01582   // if the cursor is on the lefmost position of an element
01583   // which is preceded by a non-mcOperator element...
01584   if (mgui_nCursorPos > 0 && !data_isOp(mgui_nCursorPos-1)) {
01585    
01586    // then, move cursor left...
01587    gui_MoveCursorLeft();
01588    /*mgui_nCursorPos--;
01589    data_Get(mgui_nCursorPos)->gui_SetCursorPos(mcCP_END);*/
01590   }
01591  }
01592  
01593 
01594  // check if we have to change focus
01595  switch (result) {
01596  case mcMCR_SETFOCUS_PREVIOUS:
01597 
01598   if (mgui_nCursorPos > 0) {
01599 
01600    // if the cursor is placed on the leftmost point of the focused
01601    // element:
01602    //              1234ab*123|d     ('d' is the focused element)
01603    // and there is no operator between the focused element and the
01604    // previous one, we must then not only set the focus on the previous
01605    // element but also move the cursor left inside it:
01606    //              1234ab*12|3d     ('123' is now the focused element)
01607    bool b = !data_isOp(mgui_nCursorPos) && !data_isOp(mgui_nCursorPos-1);
01608 
01609    gui_MoveCursorLeft();
01610    if (b) gui_MoveCursor(mcMCF_LEFT, modifiers);
01611 
01612   } else {
01613 
01614    // can't set focus on previous element because it
01615    // doesn't exist in this polynomial
01616    return mcMCR_SETFOCUS_PREVIOUS;
01617   }
01618   break;
01619 
01620  case mcMCR_SETFOCUS_NEXT:
01621 
01622   if (mgui_nCursorPos < data_GetCount()-1) {
01623 
01624    // like for mcMCR_SETFOCUS_PREVIOUS, also in this case we must check
01625    // if we have to move cursor right again after the gui_MoveCursorRight()
01626    // call....
01627    bool b = !data_isOp(mgui_nCursorPos) && !data_isOp(mgui_nCursorPos+1);
01628 
01629    gui_MoveCursorRight();
01630    if (b) gui_MoveCursor(mcMCF_RIGHT, modifiers);
01631 
01632   } else {
01633 
01634    // can't set focus on next element because it
01635    // doesn't exist in this polynomial
01636    return mcMCR_SETFOCUS_NEXT;
01637   }
01638   break;
01639 
01640   // cannot move cursor up or down...
01641  case mcMCR_SETFOCUS_BELOW:
01642   return mcMCR_SETFOCUS_BELOW;
01643  case mcMCR_SETFOCUS_ABOVE:
01644   return mcMCR_SETFOCUS_ABOVE;
01645  }
01646 
01647  return mcMCR_OKAY;
01648 }
01649 
01650 int mcElementArrayHelpers::gui_ExMoveCursorUsingPoint(wxDC &hDC, const wxPoint &pt, int cl)
01651 {
01652  // do some checks to avoid useless work
01653  if (pt.x > gui_GetWidth() || pt.y > gui_GetHeight())
01654   return mcMCR_CANNOT_SETFOCUS;
01655 
01656  // find the element which contains the given point
01657  if (cl == -1) cl = gui_GetCenterLine();
01658  int w=0;
01659  wxRect rc;
01660 
01661  for (int i=0; i < data_GetCount(); i++) {
01662 
01663   // get element BB
01664   w += gui_GetBB(i, &rc, cl, w);
01665 
01666   // check if user clicked inside this element
01667   if (rc.Inside(pt) == TRUE) {
01668 
01669    // our search is finished !!! We have found the clicked element;
01670    // just update his cursor variables and return...
01671 
01672    // warning: user cannot select an operator
01673    if (data_isOp(i)) {
01674 
01675     // cannot set focus on an operator... set focus
01676     // on the previous element...
01677     mgui_nCursorPos = (i-1 >= 0) ? i-1 : i+1;
01678     data_Get(mgui_nCursorPos).gui_LetInCursor(mcCP_END);
01679 
01680     // ok, we set the cursor
01681     return mcMCR_OKAY;
01682    }
01683 
01684    // update cursor variable
01685    mgui_nCursorPos = i;
01686    gui_CheckCursorPos();
01687 
01688    // update also element's variables
01689    wxPoint p(pt.x-rc.x-gui_GetSpaceBetween(), pt.y-rc.y);
01690    data_Get(mgui_nCursorPos).gui_MoveCursorUsingPoint(hDC, p);
01691 
01692    // finished processing the message
01693    return mcMCR_OKAY;
01694   }
01695  }
01696 
01697  // given point is not inside an element... sorry
01698  return mcMCR_CANNOT_SETFOCUS;
01699 }
01700 
01701 void mcElementArrayHelpers::gui_GetCursorPos(mcCursorPos &cp) const
01702 {
01703  // check for begin pos
01704  if (data_GetCount() == 0) {
01705   cp.gui_Push(mcCP_BEGINEND); // not a begin nor an end...
01706   return;
01707  }
01708 
01709  if (mgui_nCursorPos == 0) {
01710   mcCursorPos r(data_Get(0).gui_GetCursorPos());
01711 
01712   if (r.isBegin() || r.isBeginEnd()) {
01713    cp.gui_Push(mcCP_BEGIN);
01714    return;
01715   }
01716  }
01717 
01718  // if the first element of the array is an operator, the cursor cannot
01719  // reach it: thus, we must consider it placed at the beginning when
01720  // mgui_nCursorPos == 1
01721  if (mgui_nCursorPos == 1 && data_isOp(0)) {
01722   mcCursorPos r(data_Get(1).gui_GetCursorPos());
01723 
01724   if (r.isBegin() || r.isBeginEnd()) {
01725    cp.gui_Push(mcCP_BEGIN);
01726    return;
01727   }
01728  }
01729 
01730  // check for end pos
01731  int n = data_GetCount()-1;
01732  if (mgui_nCursorPos == n) {
01733   mcCursorPos r(data_Get(n).gui_GetCursorPos());
01734 
01735   if (r.isEnd() || r.isBeginEnd()) {
01736    cp.gui_Push(mcCP_END);
01737    return;
01738   }
01739  }
01740 
01741  //return mcCP_INSIDE;
01742  data_Get(mgui_nCursorPos).gui_GetCursorPos(cp);
01743 }
01744 
01745 void mcElementArrayHelpers::gui_SetCursorPos(const mcCursorPos &code)
01746 {
01747  if (data_isArrayEmpty())
01748   return;   // cannot move cursor anywhere in this case !!!
01749 
01750  // move the cursor at the beginning or the end of the array
01751  if (code.isBegin()) {
01752 
01753   mgui_nCursorPos = 0;
01754 
01755   // check also that the cursor has not been placed on an operator
01756   if (data_isOp(mgui_nCursorPos))
01757    mgui_nCursorPos++;
01758   data_Get(mgui_nCursorPos).gui_SetCursorPos(mcCP_BEGIN);
01759   
01760  } else if (code.isEnd()) {
01761 
01762   mgui_nCursorPos = data_GetCount()-1;
01763 
01764   // check also that the cursor has not been placed on an operator
01765   if (data_isOp(mgui_nCursorPos))
01766    mgui_nCursorPos--;
01767   data_Get(mgui_nCursorPos).gui_SetCursorPos(mcCP_END);
01768  
01769  } else {
01770 
01771   mcASSERT(0, wxT("Couldn't accept these flags"));  
01772  }
01773 }
01774 
01775 mcElement mcElementArrayHelpers::gui_GetSelection() const
01776 {
01777  int n = gui_GetSelElemCount();
01778 
01779  // no selection ?
01780  if (n == 0)
01781   return mcEmptyElement;
01782 
01783  if (n == 1) {
01784   // if just one element is selected, we must then return its
01785   // selected subelements....
01786   mcElement tmp(gui_GetSelElem(0).gui_GetSelection());
01787   mcASSERT(tmp != mcEmptyElement, wxT("Something wrong"));
01788 
01789   // if the selection is already a mcPolynomial or an element of
01790   // this same type, we do not need to modify it (it's the only
01791   // element selected)...
01792   if (tmp.data_GetType() == mcET_POLYNOMIAL || 
01793    tmp.data_GetType() == data_GetType())
01794    return tmp;
01795 
01796   // go on encapsulating the result in a mcElementArray of this same type
01797   // restart from zero...
01798  }
01799 
01800  // more than one element is selected: we must create another
01801  // mcElementArray containing only selected elements
01802  mcElementArray sel(mcElementHelpers::data_NewElem(data_GetType()));
01803 
01804  // scan the selected elements and insert them into the
01805  // array we will return...
01806  for (int i=0; i < n; i++) {
01807 
01808   mcElement toinsert(gui_GetSelElem(i).gui_GetSelection());
01809   mcASSERT(toinsert != mcEmptyElement, wxT("We should have a valid selection !"));
01810 
01811   toinsert.data_MakePrivateCopy();
01812   sel.data_AddElements(&toinsert, 1, -1, TRUE);
01813  }
01814 
01815  return sel;
01816 }
01817 
01818 int mcElementArrayHelpers::gui_GetActiveElemIndex(int x, int y, const wxPoint &pt, int cl)
01819 {
01820  wxRect rc;
01821  int w=0;
01822 
01823  // find the highest y value for the center line
01824  if (cl == -1) cl = gui_GetCenterLine();
01825 
01826  // draw all the factors of this monomial
01827  for (int i=0; i < data_GetCount(); i++) {
01828 
01829   // get the element bounding box for the i-th element
01830   w += gui_GetBB(i, &rc, cl, w);
01831 
01832   // offset rect to get correct coord.
01833   rc.Offset(x, y);
01834 
01835   // does the rect of this element contain the mouse cursor ?
01836   if (rc.Inside(pt)) {
01837    if (data_Get(i).gui_isSelected())
01838     return mcDRW_ONSELECTION;
01839    return i;
01840   }
01841  }
01842 
01843  // no elements currently active
01844  return mcDRW_NOACTIVEELEM;
01845 }
01846 
01847 void mcElementArrayHelpers::gui_AddNewOp(mcElementType nOpType, int pos)
01848 {
01849  // this function is different from mcElementArrayHelpers::data_AddNewOp:
01850  // check to believe... :-)
01851  mcKey fake(mcOperatorHelpers::data_GetOpSymbol(nOpType).GetChar(0));
01852  gui_AddNewElement(nOpType, fake, pos);
01853 }
01854 
01855 
01856 
01857  /*mcCursorPos cp(data_Get(mgui_nCursorPos));
01858 
01859  if (cp.isInside() || cp.isUndefined()) {
01860 
01861   return FALSE;
01862  }
01863 
01864  // the cursor is placed on the begin/end of an element.
01865  if (cp.isBegin()) {
01866 
01867 
01868  }
01869 
01870  if (cp.isEnd()) {
01871 
01872   data_MoveElemRight(mgui_nCursorPos);
01873   //data_AddElements(
01874   if (toinsert.data_GetType() == mcET_POLYNOMIAL)
01875  }*/
01876 
01877 
01878 
01879 
01880 // ----------------------------------------
01881 // mcELEMENTARRAYIO
01882 // ----------------------------------------
01883 
01884 wxString mcElementArrayHelpers::io_GetInlinedExpr() const
01885 {
01886  wxString str;
01887 
01888  // concatenate the inlined expressions which compose the math contents
01889  for (int i=0; i < data_GetCount(); i++)
01890   str += data_Get(i).io_GetInlinedExpr();
01891 
01892  return str;
01893 }
01894 
01895 
01896 
01897 
01898 
01899 // ----------------------------------------
01900 // mcELEMENTARRAYMATH - miscellaneous
01901 // ----------------------------------------
01902 
01903 void mcElementArrayHelpers::math_EmbedInBracketAndRaiseTo(const mcRealValue &n)
01904 { math_EmbedInBracket().data_GetBracket().math_RaiseTo(mcPolynomial(n)); }
01905 
01906 void mcElementArrayHelpers::math_EmbedInBracketAndRaiseTo(const mcPolynomial &p)
01907 { math_EmbedInBracket().data_GetBracket().math_RaiseTo(p); }
01908 
01909 void mcElementArrayHelpers::math_EmbedInRadicalAndRaiseTo(const mcPolynomial &p)
01910 { math_EmbedInRadical().data_GetBracket().math_RaiseTo(p); }
01911 
01912 int mcElementArrayHelpers::math_GetIndexOf(int occ, const mcElement &tofind) const
01913 {
01914  int idx = math_NonRecursiveFindInChildren(occ, tofind);
01915  if (idx == -1) return -1;
01916 
01917  // VERY IMPORTANT: we cannot just return "idx" since it's in children coord;
01918  //                 we must first convert it to math coordinates...
01919  return math_DataToMathIdx(idx);
01920 }
01921 
01922 mcArrayEntry mcElementArrayHelpers::math_WrapSymbol(const mcSymbolProperties *sym)
01923 {
01924  mcSymbol s(sym->data_GetSafeLinkedSym());
01925  mcASSERT(s.data_isOk(), wxT("Invalid safe symbol ?"));
01926  return math_WrapSimple(s);
01927 }
01928 
01929 mcArrayEntry mcElementArrayHelpers::math_WrapNumber(const mcRealValue &val)
01930 {
01931  mcNumber n(val);
01932  return math_WrapSimple(n);
01933 }
01934 
01935 const mcSymbolProperties *mcElementArrayHelpers::math_GetWrappedSymbol() const
01936 {
01937  mcSymbol sym(math_GetWrapped(mcET_SYMBOL));
01938  if (sym != mcEmptyElement)
01939   return sym.data_GetProperties();
01940  return NULL;
01941 }
01942 
01943 mcRealValue mcElementArrayHelpers::math_GetWrappedNumber() const
01944 {
01945  const mcNumber num(math_GetWrapped(mcET_NUMBER));
01946  if (num != mcEmptyElement)
01947   return num.data_Get();
01948  return *mcRealValue::pNAN;
01949 }
01950 
01951 void mcElementArrayHelpers::math_ResetToOne()
01952 {
01953  // reset 
01954  data_DeleteAll();
01955 
01956  // and add a mcNumber set to 1
01957  mcASSERT(mcNumberHelpers::smath_pOne, wxT("All statics should be ready..."));
01958  math_WrapSimple(*mcNumberHelpers::smath_pOne);
01959 }
01960 
01961 void mcElementArrayHelpers::math_ResetToZero()
01962 {
01963  // reset 
01964  data_DeleteAll();
01965 
01966  // and add a mcNumber set to 0
01967  mcASSERT(mcNumberHelpers::smath_pZero, wxT("All statics should be ready..."));
01968  math_WrapSimple(*mcNumberHelpers::smath_pZero);
01969 }
01970 
01971 mcArrayEntry mcElementArrayHelpers::math_EmbedInBracket()//int *n, int count)
01972 {
01973  // allocate a new mcBracket
01974  mcBracket br;
01975  mcPolynomial pol(math_GetPolynomialWrapper());
01976  br.data_SetContent(pol);
01977 
01978  // clear all the old contents
01979  data_DeleteAll();
01980  
01981  // this object will take care of the bracket...
01982  mcArrayEntry ourb = math_WrapSimple(br); 
01983  mcASSERT(ourb.data_GetRef().data_GetType() == mcET_BRACKET, wxT("What is this ?!?!?"));
01984 
01985  return ourb;
01986 }
01987 
01988 mcArrayEntry mcElementArrayHelpers::math_EmbedInRadical()
01989 {
01990  // allocate a new mcRadical
01991  mcRadical rad;
01992  mcPolynomial pol(math_GetPolynomialWrapper());
01993  rad.data_SetContent(pol);
01994 
01995  // clear all the old contents
01996  data_DeleteAll();
01997  
01998  // this object will take care of the bracket...
01999  mcArrayEntry ourb = math_WrapSimple(rad); 
02000  mcASSERT(ourb.data_GetRef().data_GetType() == mcET_RADICAL, wxT("What is this ?!?!?"));
02001 
02002  return ourb;
02003 }
02004 
02005 mcArrayEntry mcElementArrayHelpers::math_EmbedInFraction(bool bAsDenominator)
02006 {
02007  // create a mcFraction
02008  mcFraction pelem;
02009  mcPolynomial pol = math_GetPolynomialWrapper();
02010  
02011  if (bAsDenominator) {
02012 
02013   // create a numerator containing only a mcNumber(1) and a denominator
02014   // containing a copy of this polynomial
02015   pelem.data_SetDen(pol);
02016   pelem.data_SetNum(*mcPolynomialHelpers::smath_pOne);
02017 
02018  } else {
02019 
02020   // do viceversa
02021   pelem.data_SetNum(pol);
02022   pelem.data_SetDen(*mcPolynomialHelpers::smath_pOne);
02023  }
02024 
02025  // delete all the elements contained in this array
02026  data_DeleteAll();
02027  mcMATHLOG(wxT("mcElementArrayHelpers::math_EmbedInFraction - the fraction is [%s]"), mcTXT(pelem));
02028 
02029  // and add only the fraction we built in a new monomial
02030  mcArrayEntry ourf = math_WrapSimple(pelem);  // pelem is now contained in this array 
02031  data_Check();
02032 
02033  // return the result of our work (WHICH HAS BEEN ALREADY ADDED TO THE ARRAY !!!) 
02034  mcASSERT(ourf.data_GetRef().data_GetType() == mcET_FRACTION, wxT("What is this ?!?!?"));
02035  return ourf;
02036 }
02037 
02038 mcRealValue mcElementArrayHelpers::math_Evaluate() const
02039 {
02040  mcMATHLOG(wxT("mcElementArrayHelpers::math_Evaluate [%s]"), mcTXTTHIS);
02041 
02042  // check if this function can work correctly...
02043  if (data_isArrayEmpty())
02044   return math_GetNeutralValue();
02045 
02046  // we won't check for unknowns/parameters for two reasons:
02047  // 1) doing something like
02048  //      if (math_ContainsUnknowns() || math_ContainsParameters())
02049  //         return *mcRealValue::pNAN;
02050  //    is slow (recursive search) on big arrays
02051  // 2) doing so, we would inhibit the "evaluation mode" of mcSymbol
02052  //    (see mcSymbolProperties::m_bEvaluating for more info)
02053 
02054  // yes; it can... begin algorithm
02055  mcRealValue acc = math_Get(0).math_Evaluate();
02056  if (!acc.isValid()) return *mcRealValue::pNAN;
02057 
02058  // init the accumulation variable, checking if the array begins with
02059  // an operator and, in this case, using the neutral value of this
02060  // array applied on it...
02061  if (data_isOp(0))
02062   acc = mcOperator(data_Get(0)).math_Evaluate(math_GetNeutralValue(), acc);
02063 
02064  for (int i=0, max=math_GetCount()-1; i < max; i++) {
02065 
02066   // accumulate in the "acc" variable the already
02067   // computed symbols...
02068   mcRealValue tmp = math_Get(i+1).math_Evaluate();
02069 
02070   // do not proceed if one of the operand is not valid...  
02071   if (tmp.isNAN() || acc.isNAN())
02072    return *mcRealValue::pNAN;
02073 
02074   acc = math_GetOpBetween(i, i+1).math_Evaluate(acc, tmp);
02075  }
02076  
02077  return acc;
02078 }
02079 
02080 mcBasicOpRes mcElementArrayHelpers::math_MakeReciprocal(mcElement *)
02081 {
02082  // just embed ourselves in the denominator of a fraction 
02083  math_EmbedInFraction(TRUE);
02084 
02085  return mcBOR_REMOVE_OPERAND;
02086 }
02087 
02088 int mcElementArrayHelpers::math_ReorderElements(mcElement *arr, int nelements, 
02089           int start, int toskip)
02090 {
02091  int mov = 0;
02092 
02093  // alphabetically reorder the sequence of mcSymbols using the
02094  // INSERTION SORT algorithm; for more info on the algorithm please 
02095  // refer to the good explanation at:
02096  //   http://www.wikipedia.org/wiki/Insertion_sort
02097  //
02098  // I decided to use this algorithm because:
02099  // 1) it's simple to implement also on our special array
02100  // 2) it's really efficient for a small number of elements to sort
02101  //    (which is usually our case)
02102  // 3) it works in place: that is, this algorithm does not require
02103  //    any extra memory to run
02104 
02105  // this algorithm will work only if among the entries from
02106  // symstart to symstart+nsymbols there are no operators
02107  for (int i=start; i < nelements; i+=toskip) {
02108 
02109   mcElement removedop(mcEmptyElement);
02110   mcElement removed = arr[i];
02111   if (i > 0 && mcOperatorHelpers::data_isOp(arr[i-1].data_GetType())) 
02112    removedop = arr[i-1];
02113 
02114   int insert = i;
02115   
02116   // we must put "removed" before all those elements which 
02117   // are greater than it and after all those elements which
02118   // are lesser than it:
02119   //
02120   // .--------------------------------------------------------------. 
02121   // | elements     | new position for | elements    | old position |
02122   // | <= wxT("removed") |    wxT("removed")     | > wxT("removed") | of "removed" |
02123   // .--------------------------------------------------------------
02124   // 
02125   while (insert > start) { 
02126    
02127    // get the other comparison element
02128    mcElement previous = arr[insert-toskip];   
02129    mcASSERT(!mcOperatorHelpers::data_isOp(previous.data_GetType()),
02130      wxT("I've found two adiacent operators..."));
02131 
02132    // do we have to continue to shift the array ?
02133    bool cont = removed.math_isListedBeforeOf(previous);
02134    if (!cont) break;
02135 
02136    // shift the [insert-1]-th element one place right
02137    arr[insert] = arr[insert-toskip];
02138    if (insert-toskip > 0)
02139     arr[insert-1] = arr[insert-toskip-1];
02140    insert-=toskip;
02141 
02142    // remember how many movements we did...
02143    mov++;
02144   }
02145 
02146   if (removedop != mcEmptyElement) {
02147    
02148    // place operator before the ordinary elements
02149    mcASSERT(insert > 0, wxT("Something wrong"));
02150    arr[insert-1] = removedop;   
02151   }
02152 
02153   // new position for the removed element
02154   arr[insert] = removed;
02155  }
02156 
02157  return mov;
02158 }
02159 
02160 bool mcElementArrayHelpers::math_isWrappingOnly(mcElementType t) const
02161 {
02162  // check that we contains only an element of the given type:
02163  // data_isWrappingOnly does the same but checking also for mcOperators
02164  // that we ignore here...
02165  if (math_GetCount() > 1)
02166   return FALSE;
02167  if (data_Get(0).data_GetType() == t)
02168   return TRUE;
02169  return FALSE;
02170 }
02171 
02172 const mcElement &mcElementArrayHelpers::math_GetWrapped(mcElementType t) const
02173 {
02174  if (!math_isWrappingOnly(t))
02175   return mcEmptyElement;
02176  return math_Get(0);
02177 }
02178 
02179 
02180 
02181 
02182 
02183 // ----------------------------------------
02184 // mcELEMENTARRAYMATH - simplification
02185 // ----------------------------------------
02186 
02187 mcExpSimRes mcElementArrayHelpers::math_HandleExpSimFlag(mcExpSimRes r, 
02188                mcElement *pnew, int i)
02189 {
02190  // we need i to be in DATA coord since we're going to use a lot of data functions
02191  //i = math_MathToDataIdx(i);
02192  data_CheckIndex(i);
02193  
02194  switch (r) {
02195  case mcESR_REPLACE_THIS:
02196   mcASSERT(pnew, wxT("What should I use as replacement ?"));
02197   if ((*pnew).data_GetType() == data_GetType()) {
02198    
02199    // remove the i-th element
02200    data_Delete(i);
02201    data_MoveElemLeft(i);
02202    
02203    // replace it with all the elements contained in the monomial...
02204    mcElementArray m(*pnew);
02205    data_AddElements(m.data_GetArray(), m.data_GetCount(), i);
02206 
02207    // we did not copy the elements; we just attached its element
02208    // (for better performances) to *this, so we don't want to
02209    // immediately delete pnew (because this would imply the
02210    // deletion of all its elements):
02211    m.data_DetachAll();
02212    
02213   } else {
02214    
02215    // this should be a simple element
02216    data_AddElements(pnew, 1, i, TRUE);
02217   }
02218 
02219   return mcESR_NOTFINISHED;  // still to work...
02220   
02221  case mcESR_DELETE_THIS:
02222   data_Delete(i);
02223   data_MoveElemLeft(i);
02224   return mcESR_NOTFINISHED;  // still to work...
02225   
02226  case mcESR_NOTFINISHED:
02227   return mcESR_NOTFINISHED;  // still to work... 
02228   
02229  case mcESR_DONE:
02230   break;
02231 
02232  case mcESR_INVALID_DATA:
02233  case mcESR_DISTRIBUTE:
02234   mcASSERT(0, wxT("Cannot handle this return flag..."));
02235 
02236  default:
02237   mcASSERT(0, wxT("Unhandled return flag"));
02238  }
02239  
02240  // the element is completely simplified/expanded
02241  return mcESR_DONE;
02242 }
02243 
02244 #define mcIMPLEMENT_EXPSIM_FUNCTION(x, y, cond)       \
02245 mcExpSimRes mcElementArrayHelpers::x(long flags, mcElement *pp)   \
02246 {                  \
02247  bool stilltowork = FALSE;           \
02248  mcElement pnew;              \
02249                   \
02250  mcMATHLOG(wxT("mcElementArrayHelpers::") wxT(#x)     \
02251           wxT(" [%s]"), mcTXTTHIS);   \
02252                   \
02253  /* NOTE: cannot store data_GetCount() in a variable like "max" */ \
02254  /*       and check wxT("i") against it because the number of elements */ \
02255  /*       contained can change after first loop.     */ \
02256  for (int i=0; i < data_GetCount(); i++) {       \
02257                   \
02258   /* If "cond" is constant a warning W8066 will be generated by BCC */ \
02259   if (cond) {              \
02260    mcMATHLOG(wxT("mcElementArrayHelpers::") wxT(#x)   \
02261     wxT(" - I won't process [%s]"), mcTXT(data_Get(i))); \
02262    continue;             \
02263   }                \
02264                   \
02265   mcMATHLOG(wxT("mcElementArrayHelpers::") wxT(#x)    \
02266    wxT(" - I'm going to process [%s]"), mcTXT(data_Get(i))); \
02267   mcExpSimRes r = data_Get(i).y(flags, &pnew);     \
02268                   \
02269   /* this function will return only three values */    \
02270   /* (mcESR_DONE, mcESR_NOTFINISHED, mcESR_REPLACE_THIS,  */  \
02271   /* mcESR_DISTRIBUTE) handling all the remaining ones... */  \
02272   mcExpSimRes res = math_HandleExpSimFlag(r, &pnew, i);   \
02273                   \
02274   /* check if it was not completely simplified */     \
02275   switch (res) {             \
02276   case mcESR_NOTFINISHED:           \
02277    stilltowork = TRUE;           \
02278    break;              \
02279   case mcESR_CHANGE_SIGN:           \
02280    return mcESR_CHANGE_SIGN;         \
02281   case mcESR_REPLACE_THIS:          \
02282    mcASSERT(pp, wxT("Need a valid pointer"));     \
02283    *pp=pnew;             \
02284    return mcESR_REPLACE_THIS;         \
02285   case mcESR_DISTRIBUTE:           \
02286    mcASSERT(pp, wxT("Need a valid pointer"));     \
02287    *pp=pnew;             \
02288    return mcESR_DISTRIBUTE;         \
02289                   \
02290   default:              \
02291    /* math_HandleExpSimFlag should have already */    \
02292    /* handled all other flags...   */     \
02293    mcASSERT(res == mcESR_DONE,         \
02294     wxT("Bug in math_HandleExpSimFlag ?"));     \
02295    break;              \
02296   }                \
02297  }                 \
02298                   \
02299  /* check that we did no errors */         \
02300  data_Check();              \
02301                   \
02302  /* stop here this simplification step, if our elements must */  \
02303  /* still be completely simplified/expanded...   */   \
02304  if (stilltowork)             \
02305   return mcESR_NOTFINISHED;          \
02306  return mcESR_DONE;             \
02307 }
02308 
02309 mcIMPLEMENT_EXPSIM_FUNCTION(math_SimplifyAll, math_Simplify, 0)
02310 mcIMPLEMENT_EXPSIM_FUNCTION(math_SimplifyExp, math_Expand, 
02311        !math_SimplifyNeedExp(flags, i))
02312 
02313 bool mcElementArrayHelpers::math_SimplifyNeedExp(long flags, int i) const
02314 {
02315  const mcElement &p = data_Get(i);
02316 
02317  if (flags & mcEXPSIM_KEEP_FACTORIZATION)
02318   return FALSE;
02319 
02320  // in cases like:
02321  //
02322  //      k(x-a)+jx
02323  //
02324  // we need to expand (x-a)
02325  if (p.math_ContainsSymbols())
02326   return TRUE;
02327  return FALSE;
02328 }
02329 
02330 mcExpSimRes mcElementArrayHelpers::math_SimplifyRemoveNeutrals(long flags)
02331 {
02332  mcMATHLOG(wxT("mcElementArrayHelpers::math_SimplifyRemoveNeutrals"));
02333  bool stilltowork = FALSE;
02334 
02335  // the cycle must be started only if there are more than 1 elements
02336  // in the array: if there is only one, then, even if it is a 
02337  // neutral element, it cannot be removed because this array would
02338  // otherwise be empty
02339  if (data_GetCount() > 1) {
02340 
02341   // remove neutral elements from the array
02342   for (int i=0; i < data_GetCount(); i++) {
02343   
02344    // the following test is obviously not completely
02345    // exact: it is possible to imagine a very complex
02346    // element which evaluates in the end to the neutral
02347    // value but cannot be calculated completely by math_Evaluate().
02348    if (data_Get(i).math_EvaluatesTo(math_GetNeutralValue())) {
02349     
02350     // remove the i-th element and its preceding op
02351     mcLOG(wxT("mcElementArrayHelpers::math_SimplifyRemoveNeutrals -")
02352          wxT(" removing the %dth element"), i);
02353     math_Remove(math_DataToMathIdx(i));
02354     stilltowork = TRUE;
02355    }
02356   }
02357  }
02358 
02359  if (stilltowork)
02360   return mcESR_NOTFINISHED;
02361  return mcESR_DONE;
02362 }
02363 
02364 void mcElementArrayHelpers::math_HandleBasicOpRes(mcBasicOpRes res, mcElement replacement, 
02365              int repidx)
02366 {
02367  mcMATHLOG(wxT("mcElementArrayHelpers::math_HandleBasicOpRes [%s]"), mcTXTTHIS);
02368  
02369  mcElementType newop = mcET_INVALID;
02370  if (res == mcBOR_REPLACE_OPERAND_AND_SET_MULTOP) newop = mcET_MULTOP;
02371  if (res == mcBOR_REPLACE_OPERAND_AND_SET_DIVOP) newop = mcET_DIVOP;
02372  if (res == mcBOR_REPLACE_OPERAND_AND_SET_ADDOP) newop = mcET_ADDOP;
02373  if (res == mcBOR_REPLACE_OPERAND_AND_SET_SUBOP) newop = mcET_SUBOP;
02374 
02375  switch (res) {
02376  case mcBOR_INVALID:
02377  default:
02378   mcASSERT(0, wxT("Something is wrong... cannot handle this flag"));
02379   break;
02380   
02381  case mcBOR_REPLACE_OPERAND:
02382  case mcBOR_REPLACE_OPERAND_AND_SET_MULTOP:
02383  case mcBOR_REPLACE_OPERAND_AND_SET_DIVOP:
02384  case mcBOR_REPLACE_OPERAND_AND_SET_ADDOP:
02385  case mcBOR_REPLACE_OPERAND_AND_SET_SUBOP:
02386   {
02387    // we are going to need a DATA index and not a MATH index
02388    // as given by the caller
02389    int repdataidx = math_MathToDataIdx(repidx);
02390 
02391    // "replacement" should contain the replacement element
02392    mcASSERT(replacement != mcEmptyElement, wxT("Invalid pointer"));
02393    data_AddElements(&replacement, 1, repdataidx, TRUE);
02394    data_Check();
02395    
02396    if (newop != mcET_INVALID) {
02397     
02398     mcASSERT(repdataidx > 0, wxT("There is no operator we can change..."));
02399     
02400     // change also the operator between the element whose basic
02401     // operation's result flag we're handling and the just replaced operand...
02402     if (!data_isOp(repdataidx-1))
02403      data_MoveElemRight(repdataidx-1);
02404     else
02405      data_Delete(repdataidx-1);
02406     
02407     mcElement pnewop = mcElementHelpers::data_NewElem(newop);
02408     data_AddElements(&pnewop, 1, repdataidx-1);
02409    }
02410   }
02411   break;
02412   
02413  case mcBOR_REMOVE_OPERAND:
02414   
02415   // NOTE: in a first approach, here the operator (p)
02416   //       and the second argument (right) were removed
02417   //       with 
02418   //                         Remove(i+1);
02419   //       but now, nothing is removed: the second operand
02420   //       is transformed in a neutral element which is then
02421   //       removed in math_SimplifyRemoveNeutrals
02422 
02423   math_Remove(repidx);
02424  }
02425 }
02426 
02427 mcExpSimRes mcElementArrayHelpers::math_SimplifySolveOp(long flags)
02428 {
02429  mcMATHLOG(wxT("mcElementArrayHelpers::math_SimplifySolveOp [%s]"), mcTXTTHIS);
02430  bool stilltowork = FALSE; 
02431 
02432  // cannot store math_GetCount() because elements maybe removed in the loop
02433  for (int i=0; i < math_GetCount(); i++) {
02434   
02435   // get left operand
02436   mcElement &left = math_Get(i);
02437   
02438   for (int j=0; j < math_GetCount(); j++) {
02439 
02440    // get right one
02441    mcElement &right = math_Get(j);
02442    if (i == j) continue;
02443    
02444    // get the i-th operator of the array
02445    mcOperator p = math_GetOpPreceding(j);
02446    if (p == mcEmptyElement) continue;
02447    mcMATHLOG(wxT("mcElementArrayHelpers::math_SimplifySolveOp - I'm trying to apply ")
02448     wxT("the op [%s] between the [%s] and [%s] elements"), 
02449     mcTXT(p), mcTXT(left), mcTXT(right));
02450    
02451    // apply the op
02452    mcElement rep = mcEmptyElement;
02453    mcBasicOpRes res = p.math_Apply(left, right, &rep);
02454    if (res == mcBOR_INVALID)
02455     continue;
02456    
02457    // the operator could be applied...
02458    stilltowork = TRUE;
02459    mcMATHLOG(wxT("mcElementArrayHelpers::math_SimplifySolveOp - applied ")
02460     wxT("the op [%s] between the [%s] and [%s] elements"), 
02461     mcTXT(p), mcTXT(left), mcTXT(right));
02462    
02463    // handle res
02464    math_HandleBasicOpRes(res, rep, j);
02465   }
02466  }
02467 
02468  if (stilltowork)
02469   return mcESR_NOTFINISHED;
02470  return mcESR_DONE;
02471 }
02472 
02473 mcExpSimRes mcElementArrayHelpers::math_Simplify(long flags, mcElement *pnew)
02474 {
02475  mcExpSimRes ret;
02476 
02477 #define SIMCHECK_EXIT  if (ret != mcESR_DONE) { math_EndSimSteps(); return ret; }
02478 
02479  mcMATHLOG(wxT("mcElementArrayHelpers::math_Simplify [%s] start..."), mcTXTTHIS);
02480 
02481  // PREPARATION
02482  ret = math_BeginSimSteps();
02483  SIMCHECK_EXIT;
02484 
02485  // STEP #-1: remove neutral elements so that we have less elements to reorder
02486  ret = math_SimplifyRemoveNeutrals(flags);
02487  SIMCHECK_EXIT;
02488 
02489  // STEP #0: reorder the array
02490  ret = math_Reorder();
02491  SIMCHECK_EXIT;
02492 
02493  // STEP #1: propagate simplification calls to all the elements
02494  ret = math_SimplifyAll(flags, pnew);
02495  SIMCHECK_EXIT;
02496 
02497  // STEP #2: remove neutral elements again...
02498  ret = math_SimplifyRemoveNeutrals(flags);
02499  SIMCHECK_EXIT;
02500  
02501  // STEP #3: find the first operator which can be applied and apply it
02502  ret = math_SimplifySolveOp(flags);
02503  SIMCHECK_EXIT;
02504 
02505  // STEP #4: expand those elements non containing unknowns
02506  ret = math_SimplifyExp(flags, pnew);
02507  SIMCHECK_EXIT;
02508 
02509  // FINAL ADJUSTEMENT
02510  ret = math_FinalSimSteps();
02511  SIMCHECK_EXIT;
02512  
02513  // do some checks
02514  data_Check();
02515  data_Repair();
02516 
02517  // simplification finished
02518  mcMATHLOG(wxT("mcElementArrayHelpers::math_Simplify - ended..."));
02519  return mcESR_DONE;
02520 }
02521 
02522 
02523 
02524 
02525 
02526 
02527 // ----------------------------------------
02528 // mcELEMENTARRAYMATH - expansion
02529 // ----------------------------------------
02530 
02531 mcIMPLEMENT_EXPSIM_FUNCTION(math_ExpandAll, math_Expand, 0)
02532 
02533 mcExpSimRes mcElementArrayHelpers::math_Expand(long flags, mcElement *pnew)
02534 {
02535  mcExpSimRes ret;
02536 #define EXPCHECK_EXIT  if (ret != mcESR_DONE) { math_EndExpSteps(); return ret; }
02537 
02538  mcMATHLOG(wxT("mcElementArrayHelpers::math_Expand [%s] start..."), mcTXTTHIS);
02539 
02540  // PREPARATION 
02541  ret = math_ExpandAll(flags, pnew);
02542  EXPCHECK_EXIT
02543  
02544  // do some checks
02545  data_Check();
02546 
02547  // simplification finished
02548  return mcESR_DONE;
02549 }
02550 
02551 
02552 
02553 
02554 
02555 
02556 // ----------------------------------------
02557 // mcELEMENTARRAYMATH - basic operations
02558 // ----------------------------------------
02559 
02560 bool mcElementArrayHelpers::math_CanBeAddedWith(const mcElement &p) const
02561 {
02562  // just check the given element has the same type of this element:
02563  // mcElementArrays are general containers of elements, thus they
02564  // can be divided, added, multiplied without problems...
02565  if (p.data_GetType() == data_GetType())
02566   return TRUE;
02567  return FALSE;
02568 }
02569 
02570 bool mcElementArrayHelpers::math_CanBeDivBy(const mcElement &p) const
02571 { return math_CanBeAddedWith(p); }
02572 
02573 bool mcElementArrayHelpers::math_CanBeMultWith(const mcElement &p) const
02574 { return math_CanBeAddedWith(p); }
02575 
02576 mcElementArray mcElementArrayHelpers::math_CreateWrapperFor(const mcElement &toembed) const
02577 {
02578  // be sure not to create nested arrays
02579  if (toembed.data_GetType() == data_GetType())
02580   return mcElementArray(toembed);  // toembed is already of the same type of *this
02581 
02582  // create a temporary container of this same type and then embed the
02583  // given element inside it
02584  mcElementArray tmp = mcElementHelpers::data_NewElem(data_GetType());
02585  tmp.data_AddElements(&toembed, 1);
02586 
02587  // caller must delete this
02588  return tmp;
02589 }
02590 
02591 void mcElementArrayHelpers::math_SimpleMultiplyBy(const mcElement &p)
02592 {
02593  mcElementArray tmp = math_CreateWrapperFor(p);
02594  mcElementHelpers::math_SimpleMultiplyBy(tmp); 
02595 }
02596 
02597 void mcElementArrayHelpers::math_SimpleDivideBy(const mcElement &p)
02598 {
02599  mcElementArray tmp = math_CreateWrapperFor(p);
02600  mcElementHelpers::math_SimpleDivideBy(tmp); 
02601 }
02602 
02603 void mcElementArrayHelpers::math_SimpleAdd(const mcElement &p, bool add)
02604 {
02605  //mcASSERT(!p.IsKindOf(CLASSINFO(mcElementArray)), "Cannot nest arrays");
02606  mcElementArray tmp = math_CreateWrapperFor(p);
02607  mcElementHelpers::math_SimpleAdd(tmp, add);
02608 }
02609 
02610 
02611 
02612 
02613 
02614 
02615 // ------------------------------------------
02616 // mcELEMENTARRAYMATH - remove/add elements
02617 // ------------------------------------------
02618 
02619 bool mcElementArrayHelpers::math_Remove(int idx, bool bAddZero)
02620 {
02621  int n = math_MathToDataIdx(idx);
02622  mcASSERT(n != -1, wxT("Invalid index !"));
02623 
02624  // remove the n-th element and shift everything following left
02625  data_Delete(n);
02626  data_MoveElemLeft(n);
02627 
02628  // remove also the eventually present operator which PRECEDES
02629  // the just removed element (that is, its sign +/- or its
02630  // relation with the previous element */:)...
02631  if (n > 0 && data_isOp(n-1)) {
02632 
02633   data_Delete(n-1);
02634   data_MoveElemLeft(n-1);
02635 
02636  } else {
02637 
02638   // if there is no previous operator, then we must check if the
02639   // element we just removed was the first element of the array: 
02640   // if it was, then we must remove the operator which was (and still is)
02641   // FOLLOWING it...
02642   if (n == 0 && !data_isArrayEmpty() && data_isOp(n) && 
02643    data_Get(n).data_GetType() == math_GetNeutralOpType()) {
02644 
02645    // remove this op
02646    data_Delete(n);
02647    data_MoveElemLeft(n);
02648   }
02649   
02650   // if we left as first element a mcDivOp, then we must re-insert a
02651   // simple "1" before; if we remove it, then we would get a
02652   // mathematically incorrect result. Eg:
02653   //  
02654   // BEFORE:           x/b*c*d*e
02655   //                   ^---------- we call math_Remove(0)
02656   //
02657   // AFTER (wrong):    b*c*d*e
02658   // 
02659   // AFTER (right):    1/b*c*d*e
02660 
02661   if (n == 0 && !data_isArrayEmpty() && 
02662    data_Get(n).data_GetType() == mcET_DIVOP) {
02663 
02664    mcASSERT(data_GetType() == mcET_MONOMIAL, wxT("A mcDivOp inside a mcPolynomial ?"));
02665    data_MoveElemRight(0);
02666    data_Set(0, *mcNumberHelpers::smath_pOne);
02667   }
02668  }
02669 
02670  // if the array remains empty, don't let that CheckArray adds an empty box;
02671  // create a mcNumber initialized with zero
02672  if (data_isArrayEmpty()) {
02673 
02674   // cannot use math functions like math_Addmath_Simple() because
02675   // they would use this function... we must use Data() interface  
02676   if (bAddZero)
02677    math_WrapSimple(*mcNumberHelpers::smath_pZero);
02678   else
02679    math_WrapSimple(*mcNumberHelpers::smath_pOne);
02680 
02681   // we removed the n-th element and yes, we added a new mcNumber...
02682   return TRUE;
02683  }
02684 
02685  // we removed the n-th element but we didn't add anything
02686  return FALSE;
02687 }
02688 
02689 bool mcElementArrayHelpers::math_Delete(int n, const mcElement &elem, bool bmath_AddToZero)
02690 {
02691  // find the index of the element to delete
02692  int idx = math_NonRecursiveFindInChildren(n, elem.hlp());
02693  if (idx == -1)
02694   return FALSE;  // couldn't find the n-th occurrence of such an element
02695 
02696  math_Remove(idx, bmath_AddToZero);
02697  return TRUE;
02698 }
02699 
02700 void mcElementArrayHelpers::math_ApplyOpSimple(mcElementType optype, mcElement res, 
02701            const mcElement &factor)
02702 {
02703  mcOperator tmp = mcElementHelpers::data_NewElem(optype); 
02704  mcLOG(wxT("mcElementArrayHelpers::math_ApplyOpmath_Simple - applying the %s op"), mcTXT(tmp));
02705  
02706  tmp.math_ApplySimple(res, factor);
02707 }
02708 
02709 void mcElementArrayHelpers::math_ApplyOp(mcElementType optype, mcElement res, 
02710            const mcElement &factor, mcElement *rep)
02711 {
02712  mcOperator tmp = mcElementHelpers::data_NewElem(optype); 
02713  mcLOG(wxT("mcElementArrayHelpers::math_ApplyOp - applying the %s op"), mcTXT(tmp));
02714  
02715    tmp.math_Apply(res, factor, rep);
02716 }
02717 
02718 mcMathType mcElementArrayHelpers::math_GetMathType() const
02719 {
02720  mcMathType res(mcMTL1_POLYNOMIAL, mcMTL2_ALGEBRAIC, mcMTL3_CONSTANT);
02721 
02722  if (data_isArrayEmpty())
02723   return res;
02724 
02725  // scan the elements contained and then returns the 
02726  res = data_Get(0).math_GetMathType();
02727  for (int i=0, max=math_GetCount()-1; i < max; i++) {
02728 
02729   mcMathType next = data_Get(i+1).math_GetMathType();
02730 
02731   // a monomial must always consider only the pressing condition...  
02732   mcElementType op = math_GetOpTypeBetween(i, i+1);
02733   res.math_ApplyOp(op, next);
02734  }
02735 
02736  return res;
02737 }
02738 
02739 mcOperator &mcElementArrayHelpers::math_GetOpBetween(int n1, int n2) const
02740 {
02741  mcElement *p = mcElementHelpers::data_GetInstanceOf(math_GetOpTypeBetween(n1, n2));
02742  mcASSERT(mcOperatorHelpers::data_isOp(p->data_GetType()), 
02743    wxT("math_GetOpTypeBetween() not returning an operator type ?"));
02744  return (mcOperator &)*p;
02745 }
02746 
02747 mcOperator &mcElementArrayHelpers::math_GetOpPreceding(int n) const
02748 {
02749  mcElement *p = mcElementHelpers::data_GetInstanceOf(math_GetOpTypePreceding(n));
02750  mcASSERT(mcOperatorHelpers::data_isOp(p->data_GetType()), 
02751    wxT("math_GetOpPreceding() not returning an operator type ?"));
02752  return (mcOperator &)*p;
02753 }
02754 
02755 void mcElementArrayHelpers::math_PrepareForMathOperations(mcElementArray &) const
02756 {
02757  // here should be placed the preparative for math operations...
02758 }
02759 
02760 
02761 
02762 
02763 
02764 
02765 
02766 
02767 // ----------------------------------------
02768 // mcELEMENTARRAYMATH - comparison
02769 // ----------------------------------------
02770 
02771 void mcElementArrayHelpers::math_PrepareForComparison(mcElementArray &) const
02772 {
02773  // the given pointer should point to a work copy !!!
02774 
02775  // first of all, simplify to the maximum level the array:
02776  // this will make all the following operations easier to perform
02777  // for the math engine...
02778 
02779  // FIXME: this doesn't work yet...
02780  //p.math_MaxSimplify();
02781 }
02782 
02783 bool mcElementArrayHelpers::math_Compare(const mcElement &m, long flags) const
02784 {
02785  mcMATHLOG(wxT("mcElementArrayHelpers::math_Compare [%s] - comparing with [%s]"),
02786     mcTXTTHIS, mcTXT(m));
02787  int i;
02788  
02789  // must be of the same type of this element...
02790  if (m.data_GetType() != data_GetType())
02791   return FALSE;
02792 
02793  // create backup copies
02794  mcElementArray m1(this);
02795  mcElementArray m2(m);
02796 
02797  // prepare our work copies to the comparison only if the
02798  // comparison is "strict": in this case, different orders in *this and *m
02799  // force the two arrays to be considered different.
02800  if (flags & mcFIND_STRICT) {
02801   math_PrepareForComparison(m1);
02802   math_PrepareForComparison(m2);
02803  }
02804 
02805  // these will be used very often
02806  int m1count = m1.data_GetCount();
02807  int m2count = m2.data_GetCount();
02808 
02809  // m1 must be the largest array.!!!
02810  if (m1count < m2count) {
02811 
02812   // swap the two array pointers
02813   mcSWAP(mcElementArray, m1, m2);
02814   mcSWAP(int, m2count, m1count);
02815  }
02816 
02817  // this array of BOOLs will be used to keep memory of those
02818  // elements of m2 which have already been recognized to be
02819  // present & with the same contents in m1.
02820  // 
02821  // for example, if the initial conditions are:
02822  //
02823  // m1:       123*sqrt(2)*a*b       (i = 0)
02824  //            ^
02825  // m2:       a*sqrt(2)*123*b
02826  // b2:       0    0     0  0
02827  //
02828  // then, when "123" of m1 is recognized to be in m2,
02829  // 
02830  // m1:       123*sqrt(2)*a*b       (i = 1)
02831  //                  ^
02832  // m2:       a*sqrt(2)*123*b
02833  // b2:       0    0     1  0
02834  // 
02835  bool *b2 = new bool[m2count];
02836  for (i=0; i < m2count; i++)
02837   b2[i] = FALSE;
02838 
02839  // check if each element has an element with the same content
02840  // in the given array
02841  for (i=0; i < m1count; i++) {
02842 
02843   if (m1.data_isOp(i))
02844    continue;  // skip operators...
02845 
02846   mcElementType t = m1.data_Get(i).data_GetType();
02847   mcElementType opt = m1.data_GetOpTypePreceding(i);
02848   bool matched = FALSE;
02849 
02850   // search an element in the other array of the same type
02851   for (int c=0, max=m2.data_GetNumOfElemType(t); c < max; c++) {
02852   
02853    // get the index of the c-th element of the m2 array
02854    int j = m2.data_GetElemIndexOfType(c, t);
02855 
02856    // be sure we haven't set a corrispondence with this element yet
02857    if (b2[j] == TRUE)
02858     continue; // skip this element...
02859 
02860    // check also that the operator which precedes the j-th element in m2
02861    // is of the same type of the operator which precedes the i-th element in m1
02862    mcElementType opt2 = m2.data_GetOpTypePreceding(j);
02863    if (opt != opt2)
02864     continue; // skip this element...
02865 
02866    // now, check if it has the same content of the i-th element of m1
02867    bool same = FALSE;
02868    same = m1.data_Get(i).math_Compare(m2.data_Get(j), flags);
02869 
02870    if (same) {
02871 
02872     // yes, those two elements have the same content....
02873     matched = TRUE;
02874     b2[j] = TRUE;
02875     break;  // exit from the inner loop
02876    }
02877   }
02878 
02879   // check if we could find an element which has the same content
02880   // of data_Get(i)...
02881   if (!matched)
02882    break;  // if we couldn't, then stop: the two arrays are different
02883  }
02884 
02885  // clean up everything
02886  //delete m1;
02887  //delete m2;
02888  delete [] b2;
02889 
02890  // now, the two arrays contain globally the same elements only
02891  // if all the elements of the first array have been successfully
02892  // found to be present in the second array
02893  return (i == m1count);
02894 }
02895 
02896 bool mcElementArrayHelpers::math_CompareThisOnly(const mcElement &p, long flags) const
02897 { 
02898  if (!mcElementHelpers::math_CompareThisOnly(p, flags))
02899   return FALSE;
02900 
02901  // now we are sure that p is a mcElementArray-derived class
02902  //return data_GetCount() == ((mcElementArray*)p).data_GetCount(); 
02903  return TRUE;
02904 }
02905 /*
02906 mcElement mcElementArrayHelpers::math_GetLCM(const mcElement &p) const
02907 {
02908  return math_Get(0).math_GetLCM(p);
02909 
02910 
02911  mcMonomial arr(p);
02912  mcMonomial res;
02913 
02914  res.math_ResetToOne();
02915  for (int i=0; i < math_GetCount(); i++) {
02916   for (int j=0; j < arr.math_GetCount(); j++) {
02917    
02918    // we need to get the lowest common multiple between all the
02919    // same element types stored in *this and in the given monomial...
02920    if (math_Get(i).math_CompareThisOnly(arr.math_Get(j), FALSE)) {
02921 
02922     res.math_SimpleMultiplyBy(math_Get(i).math_GetLCM(math_Get(j)));
02923    }
02924   }
02925  }
02926 
02927  return res;
02928 }
02929 
02930 mcElement mcElementArrayHelpers::math_GetGCD(const mcElement &p) const
02931 {
02932  mcMonomial arr(p);
02933  mcMonomial res;
02934 
02935  res.math_ResetToOne();
02936  for (int i=0; i < math_GetCount(); i++) {
02937   for (int j=0; j < arr.math_GetCount(); j++) {
02938    
02939    // we need to get the lowest common multiple between all the
02940    // same element types stored in *this and in the given monomial...
02941    if (math_Get(i).math_CompareThisOnly(arr.math_Get(j), FALSE)) {
02942 
02943     res.math_SimpleMultiplyBy(math_Get(i).math_GetLCM(math_Get(j)));
02944    }
02945   }
02946  }
02947 
02948  return res;
02949 }*/
02950 
02951 mcMonomial mcElementArrayHelpers::math_GetFactors() const
02952 {
02953  mcMonomial res;
02954 
02955  res.math_ResetToOne();
02956  for (int i=0,max=math_GetCount(); i<max; i++) {
02957   mcMonomial factor = math_Get(i).math_GetFactors();
02958 
02959   mcMATHLOG(wxT("mcElementArrayHelpers::math_FactoreOut - I've factored out [%s]"), mcTXT(factor));
02960   res.math_GetGCD(factor);
02961   mcMATHLOG(wxT("mcElementArrayHelpers::math_FactoreOut - result currently is [%s]"), mcTXT(res));
02962  }
02963 
02964  return res;
02965 }
02966 


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

[ Top ]