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
[ Top ] |