GiNaC  1.6.2
expairseq.cpp
Go to the documentation of this file.
00001 
00005 /*
00006  *  GiNaC Copyright (C) 1999-2011 Johannes Gutenberg University Mainz, Germany
00007  *
00008  *  This program is free software; you can redistribute it and/or modify
00009  *  it under the terms of the GNU General Public License as published by
00010  *  the Free Software Foundation; either version 2 of the License, or
00011  *  (at your option) any later version.
00012  *
00013  *  This program is distributed in the hope that it will be useful,
00014  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00015  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00016  *  GNU General Public License for more details.
00017  *
00018  *  You should have received a copy of the GNU General Public License
00019  *  along with this program; if not, write to the Free Software
00020  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
00021  */
00022 
00023 #include "expairseq.h"
00024 #include "lst.h"
00025 #include "add.h"
00026 #include "mul.h"
00027 #include "power.h"
00028 #include "relational.h"
00029 #include "wildcard.h"
00030 #include "archive.h"
00031 #include "operators.h"
00032 #include "utils.h"
00033 #include "hash_seed.h"
00034 #include "indexed.h"
00035 
00036 #include <algorithm>
00037 #if EXPAIRSEQ_USE_HASHTAB
00038 #include <cmath>
00039 #endif // EXPAIRSEQ_USE_HASHTAB
00040 #include <iostream>
00041 #include <iterator>
00042 #include <stdexcept>
00043 #include <string>
00044 
00045 namespace GiNaC {
00046 
00047     
00048 GINAC_IMPLEMENT_REGISTERED_CLASS_OPT(expairseq, basic,
00049   print_func<print_context>(&expairseq::do_print).
00050   print_func<print_tree>(&expairseq::do_print_tree))
00051 
00052 
00053 
00054 // helper classes
00056 
00057 class epp_is_less
00058 {
00059 public:
00060     bool operator()(const epp &lh, const epp &rh) const
00061     {
00062         return (*lh).is_less(*rh);
00063     }
00064 };
00065 
00067 // default constructor
00069 
00070 // public
00071 
00072 expairseq::expairseq() 
00073 #if EXPAIRSEQ_USE_HASHTAB
00074     : hashtabsize(0)
00075 #endif // EXPAIRSEQ_USE_HASHTAB
00076 {}
00077 
00078 // protected
00079 
00080 #if 0
00081 
00082 void expairseq::copy(const expairseq &other)
00083 {
00084     seq = other.seq;
00085     overall_coeff = other.overall_coeff;
00086 #if EXPAIRSEQ_USE_HASHTAB
00087     // copy hashtab
00088     hashtabsize = other.hashtabsize;
00089     if (hashtabsize!=0) {
00090         hashmask = other.hashmask;
00091         hashtab.resize(hashtabsize);
00092         epvector::const_iterator osb = other.seq.begin();
00093         for (unsigned i=0; i<hashtabsize; ++i) {
00094             hashtab[i].clear();
00095             for (epplist::const_iterator cit=other.hashtab[i].begin();
00096                  cit!=other.hashtab[i].end(); ++cit) {
00097                 hashtab[i].push_back(seq.begin()+((*cit)-osb));
00098             }
00099         }
00100     } else {
00101         hashtab.clear();
00102     }
00103 #endif // EXPAIRSEQ_USE_HASHTAB
00104 }
00105 #endif
00106 
00108 // other constructors
00110 
00111 expairseq::expairseq(const ex &lh, const ex &rh)
00112 {
00113     construct_from_2_ex(lh,rh);
00114     GINAC_ASSERT(is_canonical());
00115 }
00116 
00117 expairseq::expairseq(const exvector &v)
00118 {
00119     construct_from_exvector(v);
00120     GINAC_ASSERT(is_canonical());
00121 }
00122 
00123 expairseq::expairseq(const epvector &v, const ex &oc, bool do_index_renaming)
00124   :  overall_coeff(oc)
00125 {
00126     GINAC_ASSERT(is_a<numeric>(oc));
00127     construct_from_epvector(v, do_index_renaming);
00128     GINAC_ASSERT(is_canonical());
00129 }
00130 
00131 expairseq::expairseq(std::auto_ptr<epvector> vp, const ex &oc, bool do_index_renaming)
00132   :  overall_coeff(oc)
00133 {
00134     GINAC_ASSERT(vp.get()!=0);
00135     GINAC_ASSERT(is_a<numeric>(oc));
00136     construct_from_epvector(*vp, do_index_renaming);
00137     GINAC_ASSERT(is_canonical());
00138 }
00139 
00141 // archiving
00143 
00144 void expairseq::read_archive(const archive_node &n, lst &sym_lst) 
00145 {
00146     inherited::read_archive(n, sym_lst);
00147     archive_node::archive_node_cit first = n.find_first("rest");
00148     archive_node::archive_node_cit last = n.find_last("coeff");
00149     ++last;
00150     seq.reserve((last-first)/2);
00151 
00152     for (archive_node::archive_node_cit loc = first; loc < last;) {
00153         ex rest;
00154         ex coeff;
00155         n.find_ex_by_loc(loc++, rest, sym_lst);
00156         n.find_ex_by_loc(loc++, coeff, sym_lst);
00157         seq.push_back(expair(rest, coeff));
00158     }
00159 
00160     n.find_ex("overall_coeff", overall_coeff, sym_lst);
00161 
00162     canonicalize();
00163     GINAC_ASSERT(is_canonical());
00164 }
00165 
00166 void expairseq::archive(archive_node &n) const
00167 {
00168     inherited::archive(n);
00169     epvector::const_iterator i = seq.begin(), iend = seq.end();
00170     while (i != iend) {
00171         n.add_ex("rest", i->rest);
00172         n.add_ex("coeff", i->coeff);
00173         ++i;
00174     }
00175     n.add_ex("overall_coeff", overall_coeff);
00176 }
00177 
00178 
00180 // functions overriding virtual functions from base classes
00182 
00183 // public
00184 
00185 void expairseq::do_print(const print_context & c, unsigned level) const
00186 {
00187     c.s << "[[";
00188     printseq(c, ',', precedence(), level);
00189     c.s << "]]";
00190 }
00191 
00192 void expairseq::do_print_tree(const print_tree & c, unsigned level) const
00193 {
00194     c.s << std::string(level, ' ') << class_name() << " @" << this
00195         << std::hex << ", hash=0x" << hashvalue << ", flags=0x" << flags << std::dec
00196         << ", nops=" << nops()
00197         << std::endl;
00198     size_t num = seq.size();
00199     for (size_t i=0; i<num; ++i) {
00200         seq[i].rest.print(c, level + c.delta_indent);
00201         seq[i].coeff.print(c, level + c.delta_indent);
00202         if (i != num - 1)
00203             c.s << std::string(level + c.delta_indent, ' ') << "-----" << std::endl;
00204     }
00205     if (!overall_coeff.is_equal(default_overall_coeff())) {
00206         c.s << std::string(level + c.delta_indent, ' ') << "-----" << std::endl
00207             << std::string(level + c.delta_indent, ' ') << "overall_coeff" << std::endl;
00208         overall_coeff.print(c, level + c.delta_indent);
00209     }
00210     c.s << std::string(level + c.delta_indent,' ') << "=====" << std::endl;
00211 #if EXPAIRSEQ_USE_HASHTAB
00212     c.s << std::string(level + c.delta_indent,' ')
00213         << "hashtab size " << hashtabsize << std::endl;
00214     if (hashtabsize == 0) return;
00215 #define MAXCOUNT 5
00216     unsigned count[MAXCOUNT+1];
00217     for (int i=0; i<MAXCOUNT+1; ++i)
00218         count[i] = 0;
00219     unsigned this_bin_fill;
00220     unsigned cum_fill_sq = 0;
00221     unsigned cum_fill = 0;
00222     for (unsigned i=0; i<hashtabsize; ++i) {
00223         this_bin_fill = 0;
00224         if (hashtab[i].size() > 0) {
00225             c.s << std::string(level + c.delta_indent, ' ')
00226                 << "bin " << i << " with entries ";
00227             for (epplist::const_iterator it=hashtab[i].begin();
00228                  it!=hashtab[i].end(); ++it) {
00229                 c.s << *it-seq.begin() << " ";
00230                 ++this_bin_fill;
00231             }
00232             c.s << std::endl;
00233             cum_fill += this_bin_fill;
00234             cum_fill_sq += this_bin_fill*this_bin_fill;
00235         }
00236         if (this_bin_fill<MAXCOUNT)
00237             ++count[this_bin_fill];
00238         else
00239             ++count[MAXCOUNT];
00240     }
00241     unsigned fact = 1;
00242     double cum_prob = 0;
00243     double lambda = (1.0*seq.size()) / hashtabsize;
00244     for (int k=0; k<MAXCOUNT; ++k) {
00245         if (k>0)
00246             fact *= k;
00247         double prob = std::pow(lambda,k)/fact * std::exp(-lambda);
00248         cum_prob += prob;
00249         c.s << std::string(level + c.delta_indent, ' ') << "bins with " << k << " entries: "
00250             << int(1000.0*count[k]/hashtabsize)/10.0 << "% (expected: "
00251             << int(prob*1000)/10.0 << ")" << std::endl;
00252     }
00253     c.s << std::string(level + c.delta_indent, ' ') << "bins with more entries: "
00254         << int(1000.0*count[MAXCOUNT]/hashtabsize)/10.0 << "% (expected: "
00255         << int((1-cum_prob)*1000)/10.0 << ")" << std::endl;
00256 
00257     c.s << std::string(level + c.delta_indent, ' ') << "variance: "
00258         << 1.0/hashtabsize*cum_fill_sq-(1.0/hashtabsize*cum_fill)*(1.0/hashtabsize*cum_fill)
00259         << std::endl;
00260     c.s << std::string(level + c.delta_indent, ' ') << "average fill: "
00261         << (1.0*cum_fill)/hashtabsize
00262         << " (should be equal to " << (1.0*seq.size())/hashtabsize << ")" << std::endl;
00263 #endif // EXPAIRSEQ_USE_HASHTAB
00264 }
00265 
00266 bool expairseq::info(unsigned inf) const
00267 {
00268     switch(inf) {
00269         case info_flags::expanded:
00270             return (flags & status_flags::expanded);
00271         case info_flags::has_indices: {
00272             if (flags & status_flags::has_indices)
00273                 return true;
00274             else if (flags & status_flags::has_no_indices)
00275                 return false;
00276             for (epvector::const_iterator i = seq.begin(); i != seq.end(); ++i) {
00277                 if (i->rest.info(info_flags::has_indices)) {
00278                     this->setflag(status_flags::has_indices);
00279                     this->clearflag(status_flags::has_no_indices);
00280                     return true;
00281                 }
00282             }
00283             this->clearflag(status_flags::has_indices);
00284             this->setflag(status_flags::has_no_indices);
00285             return false;
00286         }
00287     }
00288     return inherited::info(inf);
00289 }
00290 
00291 size_t expairseq::nops() const
00292 {
00293     if (overall_coeff.is_equal(default_overall_coeff()))
00294         return seq.size();
00295     else
00296         return seq.size()+1;
00297 }
00298 
00299 ex expairseq::op(size_t i) const
00300 {
00301     if (i < seq.size())
00302         return recombine_pair_to_ex(seq[i]);
00303     GINAC_ASSERT(!overall_coeff.is_equal(default_overall_coeff()));
00304     return overall_coeff;
00305 }
00306 
00307 ex expairseq::map(map_function &f) const
00308 {
00309     std::auto_ptr<epvector> v(new epvector);
00310     v->reserve(seq.size()+1);
00311 
00312     epvector::const_iterator cit = seq.begin(), last = seq.end();
00313     while (cit != last) {
00314         v->push_back(split_ex_to_pair(f(recombine_pair_to_ex(*cit))));
00315         ++cit;
00316     }
00317 
00318     if (overall_coeff.is_equal(default_overall_coeff()))
00319         return thisexpairseq(v, default_overall_coeff(), true);
00320     else {
00321         ex newcoeff = f(overall_coeff);
00322         if(is_a<numeric>(newcoeff))
00323             return thisexpairseq(v, newcoeff, true);
00324         else {
00325             v->push_back(split_ex_to_pair(newcoeff));
00326             return thisexpairseq(v, default_overall_coeff(), true);
00327         }
00328     }
00329 }
00330 
00332 ex expairseq::eval(int level) const
00333 {
00334     if ((level==1) && (flags &status_flags::evaluated))
00335         return *this;
00336     
00337     std::auto_ptr<epvector> vp = evalchildren(level);
00338     if (vp.get() == 0)
00339         return this->hold();
00340     
00341     return (new expairseq(vp, overall_coeff))->setflag(status_flags::dynallocated | status_flags::evaluated);
00342 }
00343 
00344 epvector* conjugateepvector(const epvector&epv)
00345 {
00346     epvector *newepv = 0;
00347     for (epvector::const_iterator i=epv.begin(); i!=epv.end(); ++i) {
00348         if(newepv) {
00349             newepv->push_back(i->conjugate());
00350             continue;
00351         }
00352         expair x = i->conjugate();
00353         if (x.is_equal(*i)) {
00354             continue;
00355         }
00356         newepv = new epvector;
00357         newepv->reserve(epv.size());
00358         for (epvector::const_iterator j=epv.begin(); j!=i; ++j) {
00359             newepv->push_back(*j);
00360         }
00361         newepv->push_back(x);
00362     }
00363     return newepv;
00364 }
00365 
00366 ex expairseq::conjugate() const
00367 {
00368     epvector* newepv = conjugateepvector(seq);
00369     ex x = overall_coeff.conjugate();
00370     if (!newepv && are_ex_trivially_equal(x, overall_coeff)) {
00371         return *this;
00372     }
00373     ex result = thisexpairseq(newepv ? *newepv : seq, x);
00374     delete newepv;
00375     return result;
00376 }
00377 
00378 bool expairseq::match(const ex & pattern, exmap & repl_lst) const
00379 {
00380     // This differs from basic::match() because we want "a+b+c+d" to
00381     // match "d+*+b" with "*" being "a+c", and we want to honor commutativity
00382 
00383     if (typeid(*this) == typeid(ex_to<basic>(pattern))) {
00384 
00385         // Check whether global wildcard (one that matches the "rest of the
00386         // expression", like "*" above) is present
00387         bool has_global_wildcard = false;
00388         ex global_wildcard;
00389         for (size_t i=0; i<pattern.nops(); i++) {
00390             if (is_exactly_a<wildcard>(pattern.op(i))) {
00391                 has_global_wildcard = true;
00392                 global_wildcard = pattern.op(i);
00393                 break;
00394             }
00395         }
00396 
00397         // Unfortunately, this is an O(N^2) operation because we can't
00398         // sort the pattern in a useful way...
00399 
00400         // Chop into terms
00401         exvector ops;
00402         ops.reserve(nops());
00403         for (size_t i=0; i<nops(); i++)
00404             ops.push_back(op(i));
00405 
00406         // Now, for every term of the pattern, look for a matching term in
00407         // the expression and remove the match
00408         for (size_t i=0; i<pattern.nops(); i++) {
00409             ex p = pattern.op(i);
00410             if (has_global_wildcard && p.is_equal(global_wildcard))
00411                 continue;
00412             exvector::iterator it = ops.begin(), itend = ops.end();
00413             while (it != itend) {
00414                 if (it->match(p, repl_lst)) {
00415                     ops.erase(it);
00416                     goto found;
00417                 }
00418                 ++it;
00419             }
00420             return false; // no match found
00421 found:      ;
00422         }
00423 
00424         if (has_global_wildcard) {
00425 
00426             // Assign all the remaining terms to the global wildcard (unless
00427             // it has already been matched before, in which case the matches
00428             // must be equal)
00429             size_t num = ops.size();
00430             std::auto_ptr<epvector> vp(new epvector);
00431             vp->reserve(num);
00432             for (size_t i=0; i<num; i++)
00433                 vp->push_back(split_ex_to_pair(ops[i]));
00434             ex rest = thisexpairseq(vp, default_overall_coeff());
00435             for (exmap::const_iterator it = repl_lst.begin(); it != repl_lst.end(); ++it) {
00436                 if (it->first.is_equal(global_wildcard))
00437                     return rest.is_equal(it->second);
00438             }
00439             repl_lst[global_wildcard] = rest;
00440             return true;
00441 
00442         } else {
00443 
00444             // No global wildcard, then the match fails if there are any
00445             // unmatched terms left
00446             return ops.empty();
00447         }
00448     }
00449     return inherited::match(pattern, repl_lst);
00450 }
00451 
00452 ex expairseq::subs(const exmap & m, unsigned options) const
00453 {
00454     std::auto_ptr<epvector> vp = subschildren(m, options);
00455     if (vp.get())
00456         return ex_to<basic>(thisexpairseq(vp, overall_coeff, (options & subs_options::no_index_renaming) == 0));
00457     else if ((options & subs_options::algebraic) && is_exactly_a<mul>(*this))
00458         return static_cast<const mul *>(this)->algebraic_subs_mul(m, options);
00459     else
00460         return subs_one_level(m, options);
00461 }
00462 
00463 // protected
00464 
00465 int expairseq::compare_same_type(const basic &other) const
00466 {
00467     GINAC_ASSERT(is_a<expairseq>(other));
00468     const expairseq &o = static_cast<const expairseq &>(other);
00469     
00470     int cmpval;
00471     
00472     // compare number of elements
00473     if (seq.size() != o.seq.size())
00474         return (seq.size()<o.seq.size()) ? -1 : 1;
00475     
00476     // compare overall_coeff
00477     cmpval = overall_coeff.compare(o.overall_coeff);
00478     if (cmpval!=0)
00479         return cmpval;
00480     
00481 #if EXPAIRSEQ_USE_HASHTAB
00482     GINAC_ASSERT(hashtabsize==o.hashtabsize);
00483     if (hashtabsize==0) {
00484 #endif // EXPAIRSEQ_USE_HASHTAB
00485         epvector::const_iterator cit1 = seq.begin();
00486         epvector::const_iterator cit2 = o.seq.begin();
00487         epvector::const_iterator last1 = seq.end();
00488         epvector::const_iterator last2 = o.seq.end();
00489         
00490         for (; (cit1!=last1)&&(cit2!=last2); ++cit1, ++cit2) {
00491             cmpval = (*cit1).compare(*cit2);
00492             if (cmpval!=0) return cmpval;
00493         }
00494         
00495         GINAC_ASSERT(cit1==last1);
00496         GINAC_ASSERT(cit2==last2);
00497         
00498         return 0;
00499 #if EXPAIRSEQ_USE_HASHTAB
00500     }
00501     
00502     // compare number of elements in each hashtab entry
00503     for (unsigned i=0; i<hashtabsize; ++i) {
00504         unsigned cursize=hashtab[i].size();
00505         if (cursize != o.hashtab[i].size())
00506             return (cursize < o.hashtab[i].size()) ? -1 : 1;
00507     }
00508     
00509     // compare individual (sorted) hashtab entries
00510     for (unsigned i=0; i<hashtabsize; ++i) {
00511         unsigned sz = hashtab[i].size();
00512         if (sz>0) {
00513             const epplist &eppl1 = hashtab[i];
00514             const epplist &eppl2 = o.hashtab[i];
00515             epplist::const_iterator it1 = eppl1.begin();
00516             epplist::const_iterator it2 = eppl2.begin();
00517             while (it1!=eppl1.end()) {
00518                 cmpval = (*(*it1)).compare(*(*it2));
00519                 if (cmpval!=0)
00520                     return cmpval;
00521                 ++it1;
00522                 ++it2;
00523             }
00524         }
00525     }
00526     
00527     return 0; // equal
00528 #endif // EXPAIRSEQ_USE_HASHTAB
00529 }
00530 
00531 bool expairseq::is_equal_same_type(const basic &other) const
00532 {
00533     const expairseq &o = static_cast<const expairseq &>(other);
00534     
00535     // compare number of elements
00536     if (seq.size()!=o.seq.size())
00537         return false;
00538     
00539     // compare overall_coeff
00540     if (!overall_coeff.is_equal(o.overall_coeff))
00541         return false;
00542     
00543 #if EXPAIRSEQ_USE_HASHTAB
00544     // compare number of elements in each hashtab entry
00545     if (hashtabsize!=o.hashtabsize) {
00546         std::cout << "this:" << std::endl;
00547         print(print_tree(std::cout));
00548         std::cout << "other:" << std::endl;
00549         other.print(print_tree(std::cout));
00550     }
00551         
00552     GINAC_ASSERT(hashtabsize==o.hashtabsize);
00553     
00554     if (hashtabsize==0) {
00555 #endif // EXPAIRSEQ_USE_HASHTAB
00556         epvector::const_iterator cit1 = seq.begin();
00557         epvector::const_iterator cit2 = o.seq.begin();
00558         epvector::const_iterator last1 = seq.end();
00559         
00560         while (cit1!=last1) {
00561             if (!(*cit1).is_equal(*cit2)) return false;
00562             ++cit1;
00563             ++cit2;
00564         }
00565         
00566         return true;
00567 #if EXPAIRSEQ_USE_HASHTAB
00568     }
00569     
00570     for (unsigned i=0; i<hashtabsize; ++i) {
00571         if (hashtab[i].size() != o.hashtab[i].size())
00572             return false;
00573     }
00574 
00575     // compare individual sorted hashtab entries
00576     for (unsigned i=0; i<hashtabsize; ++i) {
00577         unsigned sz = hashtab[i].size();
00578         if (sz>0) {
00579             const epplist &eppl1 = hashtab[i];
00580             const epplist &eppl2 = o.hashtab[i];
00581             epplist::const_iterator it1 = eppl1.begin();
00582             epplist::const_iterator it2 = eppl2.begin();
00583             while (it1!=eppl1.end()) {
00584                 if (!(*(*it1)).is_equal(*(*it2))) return false;
00585                 ++it1;
00586                 ++it2;
00587             }
00588         }
00589     }
00590     
00591     return true;
00592 #endif // EXPAIRSEQ_USE_HASHTAB
00593 }
00594 
00595 unsigned expairseq::return_type() const
00596 {
00597     return return_types::noncommutative_composite;
00598 }
00599 
00600 unsigned expairseq::calchash() const
00601 {
00602     unsigned v = make_hash_seed(typeid(*this));
00603     epvector::const_iterator i = seq.begin();
00604     const epvector::const_iterator end = seq.end();
00605     while (i != end) {
00606         v ^= i->rest.gethash();
00607 #if !EXPAIRSEQ_USE_HASHTAB
00608         // rotation spoils commutativity!
00609         v = rotate_left(v);
00610         v ^= i->coeff.gethash();
00611 #endif // !EXPAIRSEQ_USE_HASHTAB
00612         ++i;
00613     }
00614 
00615     v ^= overall_coeff.gethash();
00616 
00617     // store calculated hash value only if object is already evaluated
00618     if (flags &status_flags::evaluated) {
00619         setflag(status_flags::hash_calculated);
00620         hashvalue = v;
00621     }
00622     
00623     return v;
00624 }
00625 
00626 ex expairseq::expand(unsigned options) const
00627 {
00628     std::auto_ptr<epvector> vp = expandchildren(options);
00629     if (vp.get())
00630         return thisexpairseq(vp, overall_coeff);
00631     else {
00632         // The terms have not changed, so it is safe to declare this expanded
00633         return (options == 0) ? setflag(status_flags::expanded) : *this;
00634     }
00635 }
00636 
00638 // new virtual functions which can be overridden by derived classes
00640 
00641 // protected
00642 
00651 ex expairseq::thisexpairseq(const epvector &v, const ex &oc, bool do_index_renaming) const
00652 {
00653     return expairseq(v, oc, do_index_renaming);
00654 }
00655 
00656 ex expairseq::thisexpairseq(std::auto_ptr<epvector> vp, const ex &oc, bool do_index_renaming) const
00657 {
00658     return expairseq(vp, oc, do_index_renaming);
00659 }
00660 
00661 void expairseq::printpair(const print_context & c, const expair & p, unsigned upper_precedence) const
00662 {
00663     c.s << "[[";
00664     p.rest.print(c, precedence());
00665     c.s << ",";
00666     p.coeff.print(c, precedence());
00667     c.s << "]]";
00668 }
00669 
00670 void expairseq::printseq(const print_context & c, char delim,
00671                          unsigned this_precedence,
00672                          unsigned upper_precedence) const
00673 {
00674     if (this_precedence <= upper_precedence)
00675         c.s << "(";
00676     epvector::const_iterator it, it_last = seq.end() - 1;
00677     for (it=seq.begin(); it!=it_last; ++it) {
00678         printpair(c, *it, this_precedence);
00679         c.s << delim;
00680     }
00681     printpair(c, *it, this_precedence);
00682     if (!overall_coeff.is_equal(default_overall_coeff())) {
00683         c.s << delim;
00684         overall_coeff.print(c, this_precedence);
00685     }
00686     
00687     if (this_precedence <= upper_precedence)
00688         c.s << ")";
00689 }
00690 
00691 
00694 expair expairseq::split_ex_to_pair(const ex &e) const
00695 {
00696     return expair(e,_ex1);
00697 }
00698 
00699 
00700 expair expairseq::combine_ex_with_coeff_to_pair(const ex &e,
00701                                                 const ex &c) const
00702 {
00703     GINAC_ASSERT(is_exactly_a<numeric>(c));
00704     
00705     return expair(e,c);
00706 }
00707 
00708 
00709 expair expairseq::combine_pair_with_coeff_to_pair(const expair &p,
00710                                                   const ex &c) const
00711 {
00712     GINAC_ASSERT(is_exactly_a<numeric>(p.coeff));
00713     GINAC_ASSERT(is_exactly_a<numeric>(c));
00714     
00715     return expair(p.rest,ex_to<numeric>(p.coeff).mul_dyn(ex_to<numeric>(c)));
00716 }
00717 
00718 
00721 ex expairseq::recombine_pair_to_ex(const expair &p) const
00722 {
00723     return lst(p.rest,p.coeff);
00724 }
00725 
00726 bool expairseq::expair_needs_further_processing(epp it)
00727 {
00728 #if EXPAIRSEQ_USE_HASHTAB
00729     //#  error "FIXME: expair_needs_further_processing not yet implemented for hashtabs, sorry. A.F."
00730 #endif // EXPAIRSEQ_USE_HASHTAB
00731     return false;
00732 }
00733 
00734 ex expairseq::default_overall_coeff() const
00735 {
00736     return _ex0;
00737 }
00738 
00739 void expairseq::combine_overall_coeff(const ex &c)
00740 {
00741     GINAC_ASSERT(is_exactly_a<numeric>(overall_coeff));
00742     GINAC_ASSERT(is_exactly_a<numeric>(c));
00743     overall_coeff = ex_to<numeric>(overall_coeff).add_dyn(ex_to<numeric>(c));
00744 }
00745 
00746 void expairseq::combine_overall_coeff(const ex &c1, const ex &c2)
00747 {
00748     GINAC_ASSERT(is_exactly_a<numeric>(overall_coeff));
00749     GINAC_ASSERT(is_exactly_a<numeric>(c1));
00750     GINAC_ASSERT(is_exactly_a<numeric>(c2));
00751     overall_coeff = ex_to<numeric>(overall_coeff).
00752                     add_dyn(ex_to<numeric>(c1).mul(ex_to<numeric>(c2)));
00753 }
00754 
00755 bool expairseq::can_make_flat(const expair &p) const
00756 {
00757     return true;
00758 }
00759 
00760 
00762 // non-virtual functions in this class
00764 
00765 void expairseq::construct_from_2_ex_via_exvector(const ex &lh, const ex &rh)
00766 {
00767     exvector v;
00768     v.reserve(2);
00769     v.push_back(lh);
00770     v.push_back(rh);
00771     construct_from_exvector(v);
00772 #if EXPAIRSEQ_USE_HASHTAB
00773     GINAC_ASSERT((hashtabsize==0)||(hashtabsize>=minhashtabsize));
00774     GINAC_ASSERT(hashtabsize==calc_hashtabsize(seq.size()));
00775 #endif // EXPAIRSEQ_USE_HASHTAB
00776 }
00777 
00778 void expairseq::construct_from_2_ex(const ex &lh, const ex &rh)
00779 {
00780     if (typeid(ex_to<basic>(lh)) == typeid(*this)) {
00781         if (typeid(ex_to<basic>(rh)) == typeid(*this)) {
00782 #if EXPAIRSEQ_USE_HASHTAB
00783             unsigned totalsize = ex_to<expairseq>(lh).seq.size() +
00784                                  ex_to<expairseq>(rh).seq.size();
00785             if (calc_hashtabsize(totalsize)!=0) {
00786                 construct_from_2_ex_via_exvector(lh,rh);
00787             } else {
00788 #endif // EXPAIRSEQ_USE_HASHTAB
00789                 if (is_a<mul>(lh) && lh.info(info_flags::has_indices) && 
00790                     rh.info(info_flags::has_indices)) {
00791                     ex newrh=rename_dummy_indices_uniquely(lh, rh);
00792                     construct_from_2_expairseq(ex_to<expairseq>(lh),
00793                                                ex_to<expairseq>(newrh));
00794                 }
00795                 else
00796                     construct_from_2_expairseq(ex_to<expairseq>(lh),
00797                                                ex_to<expairseq>(rh));
00798 #if EXPAIRSEQ_USE_HASHTAB
00799             }
00800 #endif // EXPAIRSEQ_USE_HASHTAB
00801             return;
00802         } else {
00803 #if EXPAIRSEQ_USE_HASHTAB
00804             unsigned totalsize = ex_to<expairseq>(lh).seq.size()+1;
00805             if (calc_hashtabsize(totalsize)!=0) {
00806                 construct_from_2_ex_via_exvector(lh, rh);
00807             } else {
00808 #endif // EXPAIRSEQ_USE_HASHTAB
00809                 construct_from_expairseq_ex(ex_to<expairseq>(lh), rh);
00810 #if EXPAIRSEQ_USE_HASHTAB
00811             }
00812 #endif // EXPAIRSEQ_USE_HASHTAB
00813             return;
00814         }
00815     } else if (typeid(ex_to<basic>(rh)) == typeid(*this)) {
00816 #if EXPAIRSEQ_USE_HASHTAB
00817         unsigned totalsize=ex_to<expairseq>(rh).seq.size()+1;
00818         if (calc_hashtabsize(totalsize)!=0) {
00819             construct_from_2_ex_via_exvector(lh,rh);
00820         } else {
00821 #endif // EXPAIRSEQ_USE_HASHTAB
00822             construct_from_expairseq_ex(ex_to<expairseq>(rh),lh);
00823 #if EXPAIRSEQ_USE_HASHTAB
00824         }
00825 #endif // EXPAIRSEQ_USE_HASHTAB
00826         return;
00827     }
00828     
00829 #if EXPAIRSEQ_USE_HASHTAB
00830     if (calc_hashtabsize(2)!=0) {
00831         construct_from_2_ex_via_exvector(lh,rh);
00832         return;
00833     }
00834     hashtabsize = 0;
00835 #endif // EXPAIRSEQ_USE_HASHTAB
00836     
00837     if (is_exactly_a<numeric>(lh)) {
00838         if (is_exactly_a<numeric>(rh)) {
00839             combine_overall_coeff(lh);
00840             combine_overall_coeff(rh);
00841         } else {
00842             combine_overall_coeff(lh);
00843             seq.push_back(split_ex_to_pair(rh));
00844         }
00845     } else {
00846         if (is_exactly_a<numeric>(rh)) {
00847             combine_overall_coeff(rh);
00848             seq.push_back(split_ex_to_pair(lh));
00849         } else {
00850             expair p1 = split_ex_to_pair(lh);
00851             expair p2 = split_ex_to_pair(rh);
00852             
00853             int cmpval = p1.rest.compare(p2.rest);
00854             if (cmpval==0) {
00855                 p1.coeff = ex_to<numeric>(p1.coeff).add_dyn(ex_to<numeric>(p2.coeff));
00856                 if (!ex_to<numeric>(p1.coeff).is_zero()) {
00857                     // no further processing is necessary, since this
00858                     // one element will usually be recombined in eval()
00859                     seq.push_back(p1);
00860                 }
00861             } else {
00862                 seq.reserve(2);
00863                 if (cmpval<0) {
00864                     seq.push_back(p1);
00865                     seq.push_back(p2);
00866                 } else {
00867                     seq.push_back(p2);
00868                     seq.push_back(p1);
00869                 }
00870             }
00871         }
00872     }
00873 }
00874 
00875 void expairseq::construct_from_2_expairseq(const expairseq &s1,
00876                                            const expairseq &s2)
00877 {
00878     combine_overall_coeff(s1.overall_coeff);
00879     combine_overall_coeff(s2.overall_coeff);
00880 
00881     epvector::const_iterator first1 = s1.seq.begin();
00882     epvector::const_iterator last1 = s1.seq.end();
00883     epvector::const_iterator first2 = s2.seq.begin();
00884     epvector::const_iterator last2 = s2.seq.end();
00885 
00886     seq.reserve(s1.seq.size()+s2.seq.size());
00887 
00888     bool needs_further_processing=false;
00889     
00890     while (first1!=last1 && first2!=last2) {
00891         int cmpval = (*first1).rest.compare((*first2).rest);
00892 
00893         if (cmpval==0) {
00894             // combine terms
00895             const numeric &newcoeff = ex_to<numeric>(first1->coeff).
00896                                        add(ex_to<numeric>(first2->coeff));
00897             if (!newcoeff.is_zero()) {
00898                 seq.push_back(expair(first1->rest,newcoeff));
00899                 if (expair_needs_further_processing(seq.end()-1)) {
00900                     needs_further_processing = true;
00901                 }
00902             }
00903             ++first1;
00904             ++first2;
00905         } else if (cmpval<0) {
00906             seq.push_back(*first1);
00907             ++first1;
00908         } else {
00909             seq.push_back(*first2);
00910             ++first2;
00911         }
00912     }
00913     
00914     while (first1!=last1) {
00915         seq.push_back(*first1);
00916         ++first1;
00917     }
00918     while (first2!=last2) {
00919         seq.push_back(*first2);
00920         ++first2;
00921     }
00922     
00923     if (needs_further_processing) {
00924         epvector v = seq;
00925         seq.clear();
00926         construct_from_epvector(v);
00927     }
00928 }
00929 
00930 void expairseq::construct_from_expairseq_ex(const expairseq &s,
00931                                             const ex &e)
00932 {
00933     combine_overall_coeff(s.overall_coeff);
00934     if (is_exactly_a<numeric>(e)) {
00935         combine_overall_coeff(e);
00936         seq = s.seq;
00937         return;
00938     }
00939     
00940     epvector::const_iterator first = s.seq.begin();
00941     epvector::const_iterator last = s.seq.end();
00942     expair p = split_ex_to_pair(e);
00943     
00944     seq.reserve(s.seq.size()+1);
00945     bool p_pushed = false;
00946     
00947     bool needs_further_processing=false;
00948     
00949     // merge p into s.seq
00950     while (first!=last) {
00951         int cmpval = (*first).rest.compare(p.rest);
00952         if (cmpval==0) {
00953             // combine terms
00954             const numeric &newcoeff = ex_to<numeric>(first->coeff).
00955                                        add(ex_to<numeric>(p.coeff));
00956             if (!newcoeff.is_zero()) {
00957                 seq.push_back(expair(first->rest,newcoeff));
00958                 if (expair_needs_further_processing(seq.end()-1))
00959                     needs_further_processing = true;
00960             }
00961             ++first;
00962             p_pushed = true;
00963             break;
00964         } else if (cmpval<0) {
00965             seq.push_back(*first);
00966             ++first;
00967         } else {
00968             seq.push_back(p);
00969             p_pushed = true;
00970             break;
00971         }
00972     }
00973     
00974     if (p_pushed) {
00975         // while loop exited because p was pushed, now push rest of s.seq
00976         while (first!=last) {
00977             seq.push_back(*first);
00978             ++first;
00979         }
00980     } else {
00981         // while loop exited because s.seq was pushed, now push p
00982         seq.push_back(p);
00983     }
00984 
00985     if (needs_further_processing) {
00986         epvector v = seq;
00987         seq.clear();
00988         construct_from_epvector(v);
00989     }
00990 }
00991 
00992 void expairseq::construct_from_exvector(const exvector &v)
00993 {
00994     // simplifications: +(a,+(b,c),d) -> +(a,b,c,d) (associativity)
00995     //                  +(d,b,c,a) -> +(a,b,c,d) (canonicalization)
00996     //                  +(...,x,*(x,c1),*(x,c2)) -> +(...,*(x,1+c1+c2)) (c1, c2 numeric)
00997     //                  (same for (+,*) -> (*,^)
00998 
00999     make_flat(v);
01000 #if EXPAIRSEQ_USE_HASHTAB
01001     combine_same_terms();
01002 #else
01003     canonicalize();
01004     combine_same_terms_sorted_seq();
01005 #endif // EXPAIRSEQ_USE_HASHTAB
01006 }
01007 
01008 void expairseq::construct_from_epvector(const epvector &v, bool do_index_renaming)
01009 {
01010     // simplifications: +(a,+(b,c),d) -> +(a,b,c,d) (associativity)
01011     //                  +(d,b,c,a) -> +(a,b,c,d) (canonicalization)
01012     //                  +(...,x,*(x,c1),*(x,c2)) -> +(...,*(x,1+c1+c2)) (c1, c2 numeric)
01013     //                  same for (+,*) -> (*,^)
01014 
01015     make_flat(v, do_index_renaming);
01016 #if EXPAIRSEQ_USE_HASHTAB
01017     combine_same_terms();
01018 #else
01019     canonicalize();
01020     combine_same_terms_sorted_seq();
01021 #endif // EXPAIRSEQ_USE_HASHTAB
01022 }
01023 
01026 void expairseq::make_flat(const exvector &v)
01027 {
01028     exvector::const_iterator cit;
01029     
01030     // count number of operands which are of same expairseq derived type
01031     // and their cumulative number of operands
01032     int nexpairseqs = 0;
01033     int noperands = 0;
01034     bool do_idx_rename = false;
01035     
01036     cit = v.begin();
01037     while (cit!=v.end()) {
01038         if (typeid(ex_to<basic>(*cit)) == typeid(*this)) {
01039             ++nexpairseqs;
01040             noperands += ex_to<expairseq>(*cit).seq.size();
01041         }
01042         if (is_a<mul>(*this) && (!do_idx_rename) &&
01043                 cit->info(info_flags::has_indices))
01044             do_idx_rename = true;
01045         ++cit;
01046     }
01047     
01048     // reserve seq and coeffseq which will hold all operands
01049     seq.reserve(v.size()+noperands-nexpairseqs);
01050     
01051     // copy elements and split off numerical part
01052     make_flat_inserter mf(v, do_idx_rename);
01053     cit = v.begin();
01054     while (cit!=v.end()) {
01055         if (typeid(ex_to<basic>(*cit)) == typeid(*this)) {
01056             ex newfactor = mf.handle_factor(*cit, _ex1);
01057             const expairseq &subseqref = ex_to<expairseq>(newfactor);
01058             combine_overall_coeff(subseqref.overall_coeff);
01059             epvector::const_iterator cit_s = subseqref.seq.begin();
01060             while (cit_s!=subseqref.seq.end()) {
01061                 seq.push_back(*cit_s);
01062                 ++cit_s;
01063             }
01064         } else {
01065             if (is_exactly_a<numeric>(*cit))
01066                 combine_overall_coeff(*cit);
01067             else {
01068                 ex newfactor = mf.handle_factor(*cit, _ex1);
01069                 seq.push_back(split_ex_to_pair(newfactor));
01070             }
01071         }
01072         ++cit;
01073     }
01074 }
01075 
01078 void expairseq::make_flat(const epvector &v, bool do_index_renaming)
01079 {
01080     epvector::const_iterator cit;
01081     
01082     // count number of operands which are of same expairseq derived type
01083     // and their cumulative number of operands
01084     int nexpairseqs = 0;
01085     int noperands = 0;
01086     bool really_need_rename_inds = false;
01087     
01088     cit = v.begin();
01089     while (cit!=v.end()) {
01090         if (typeid(ex_to<basic>(cit->rest)) == typeid(*this)) {
01091             ++nexpairseqs;
01092             noperands += ex_to<expairseq>(cit->rest).seq.size();
01093         }
01094         if ((!really_need_rename_inds) && is_a<mul>(*this) &&
01095                 cit->rest.info(info_flags::has_indices))
01096             really_need_rename_inds = true;
01097         ++cit;
01098     }
01099     do_index_renaming = do_index_renaming && really_need_rename_inds;
01100     
01101     // reserve seq and coeffseq which will hold all operands
01102     seq.reserve(v.size()+noperands-nexpairseqs);
01103     make_flat_inserter mf(v, do_index_renaming);
01104     
01105     // copy elements and split off numerical part
01106     cit = v.begin();
01107     while (cit!=v.end()) {
01108         if ((typeid(ex_to<basic>(cit->rest)) == typeid(*this)) &&
01109             this->can_make_flat(*cit)) {
01110             ex newrest = mf.handle_factor(cit->rest, cit->coeff);
01111             const expairseq &subseqref = ex_to<expairseq>(newrest);
01112             combine_overall_coeff(ex_to<numeric>(subseqref.overall_coeff),
01113                                                 ex_to<numeric>(cit->coeff));
01114             epvector::const_iterator cit_s = subseqref.seq.begin();
01115             while (cit_s!=subseqref.seq.end()) {
01116                 seq.push_back(expair(cit_s->rest,
01117                                      ex_to<numeric>(cit_s->coeff).mul_dyn(ex_to<numeric>(cit->coeff))));
01118                 //seq.push_back(combine_pair_with_coeff_to_pair(*cit_s,
01119                 //                                              (*cit).coeff));
01120                 ++cit_s;
01121             }
01122         } else {
01123             if (cit->is_canonical_numeric())
01124                 combine_overall_coeff(mf.handle_factor(cit->rest, _ex1));
01125             else {
01126                 ex rest = cit->rest;
01127                 ex newrest = mf.handle_factor(rest, cit->coeff);
01128                 if (are_ex_trivially_equal(newrest, rest))
01129                     seq.push_back(*cit);
01130                 else
01131                     seq.push_back(expair(newrest, cit->coeff));
01132             }
01133         }
01134         ++cit;
01135     }
01136 }
01137 
01139 void expairseq::canonicalize()
01140 {
01141     std::sort(seq.begin(), seq.end(), expair_rest_is_less());
01142 }
01143 
01144 
01148 void expairseq::combine_same_terms_sorted_seq()
01149 {
01150     if (seq.size()<2)
01151         return;
01152 
01153     bool needs_further_processing = false;
01154 
01155     epvector::iterator itin1 = seq.begin();
01156     epvector::iterator itin2 = itin1+1;
01157     epvector::iterator itout = itin1;
01158     epvector::iterator last = seq.end();
01159     // must_copy will be set to true the first time some combination is 
01160     // possible from then on the sequence has changed and must be compacted
01161     bool must_copy = false;
01162     while (itin2!=last) {
01163         if (itin1->rest.compare(itin2->rest)==0) {
01164             itin1->coeff = ex_to<numeric>(itin1->coeff).
01165                            add_dyn(ex_to<numeric>(itin2->coeff));
01166             if (expair_needs_further_processing(itin1))
01167                 needs_further_processing = true;
01168             must_copy = true;
01169         } else {
01170             if (!ex_to<numeric>(itin1->coeff).is_zero()) {
01171                 if (must_copy)
01172                     *itout = *itin1;
01173                 ++itout;
01174             }
01175             itin1 = itin2;
01176         }
01177         ++itin2;
01178     }
01179     if (!ex_to<numeric>(itin1->coeff).is_zero()) {
01180         if (must_copy)
01181             *itout = *itin1;
01182         ++itout;
01183     }
01184     if (itout!=last)
01185         seq.erase(itout,last);
01186 
01187     if (needs_further_processing) {
01188         epvector v = seq;
01189         seq.clear();
01190         construct_from_epvector(v);
01191     }
01192 }
01193 
01194 #if EXPAIRSEQ_USE_HASHTAB
01195 
01196 unsigned expairseq::calc_hashtabsize(unsigned sz) const
01197 {
01198     unsigned size;
01199     unsigned nearest_power_of_2 = 1 << log2(sz);
01200     // if (nearest_power_of_2 < maxhashtabsize/hashtabfactor) {
01201     //  size = nearest_power_of_2*hashtabfactor;
01202     size = nearest_power_of_2/hashtabfactor;
01203     if (size<minhashtabsize)
01204         return 0;
01205 
01206     // hashtabsize must be a power of 2
01207     GINAC_ASSERT((1U << log2(size))==size);
01208     return size;
01209 }
01210 
01211 unsigned expairseq::calc_hashindex(const ex &e) const
01212 {
01213     // calculate hashindex
01214     unsigned hashindex;
01215     if (is_a<numeric>(e)) {
01216         hashindex = hashmask;
01217     } else {
01218         hashindex = e.gethash() & hashmask;
01219         // last hashtab entry is reserved for numerics
01220         if (hashindex==hashmask) hashindex = 0;
01221     }
01222     GINAC_ASSERT((hashindex<hashtabsize)||(hashtabsize==0));
01223     return hashindex;
01224 }
01225 
01226 void expairseq::shrink_hashtab()
01227 {
01228     unsigned new_hashtabsize;
01229     while (hashtabsize!=(new_hashtabsize=calc_hashtabsize(seq.size()))) {
01230         GINAC_ASSERT(new_hashtabsize<hashtabsize);
01231         if (new_hashtabsize==0) {
01232             hashtab.clear();
01233             hashtabsize = 0;
01234             canonicalize();
01235             return;
01236         }
01237         
01238         // shrink by a factor of 2
01239         unsigned half_hashtabsize = hashtabsize/2;
01240         for (unsigned i=0; i<half_hashtabsize-1; ++i)
01241             hashtab[i].merge(hashtab[i+half_hashtabsize],epp_is_less());
01242         // special treatment for numeric hashes
01243         hashtab[0].merge(hashtab[half_hashtabsize-1],epp_is_less());
01244         hashtab[half_hashtabsize-1] = hashtab[hashtabsize-1];
01245         hashtab.resize(half_hashtabsize);
01246         hashtabsize = half_hashtabsize;
01247         hashmask = hashtabsize-1;
01248     }
01249 }
01250 
01251 void expairseq::remove_hashtab_entry(epvector::const_iterator element)
01252 {
01253     if (hashtabsize==0)
01254         return; // nothing to do
01255     
01256     // calculate hashindex of element to be deleted
01257     unsigned hashindex = calc_hashindex((*element).rest);
01258 
01259     // find it in hashtab and remove it
01260     epplist &eppl = hashtab[hashindex];
01261     epplist::iterator epplit = eppl.begin();
01262     bool erased = false;
01263     while (epplit!=eppl.end()) {
01264         if (*epplit == element) {
01265             eppl.erase(epplit);
01266             erased = true;
01267             break;
01268         }
01269         ++epplit;
01270     }
01271     if (!erased) {
01272         std::cout << "tried to erase " << element-seq.begin() << std::endl;
01273         std::cout << "size " << seq.end()-seq.begin() << std::endl;
01274 
01275         unsigned hashindex = calc_hashindex(element->rest);
01276         epplist &eppl = hashtab[hashindex];
01277         epplist::iterator epplit = eppl.begin();
01278         bool erased = false;
01279         while (epplit!=eppl.end()) {
01280             if (*epplit == element) {
01281                 eppl.erase(epplit);
01282                 erased = true;
01283                 break;
01284             }
01285             ++epplit;
01286         }
01287         GINAC_ASSERT(erased);
01288     }
01289     GINAC_ASSERT(erased);
01290 }
01291 
01292 void expairseq::move_hashtab_entry(epvector::const_iterator oldpos,
01293                                    epvector::iterator newpos)
01294 {
01295     GINAC_ASSERT(hashtabsize!=0);
01296     
01297     // calculate hashindex of element which was moved
01298     unsigned hashindex=calc_hashindex((*newpos).rest);
01299 
01300     // find it in hashtab and modify it
01301     epplist &eppl = hashtab[hashindex];
01302     epplist::iterator epplit = eppl.begin();
01303     while (epplit!=eppl.end()) {
01304         if (*epplit == oldpos) {
01305             *epplit = newpos;
01306             break;
01307         }
01308         ++epplit;
01309     }
01310     GINAC_ASSERT(epplit!=eppl.end());
01311 }
01312 
01313 void expairseq::sorted_insert(epplist &eppl, epvector::const_iterator elem)
01314 {
01315     epplist::const_iterator current = eppl.begin();
01316     while ((current!=eppl.end()) && ((*current)->is_less(*elem))) {
01317         ++current;
01318     }
01319     eppl.insert(current,elem);
01320 }    
01321 
01322 void expairseq::build_hashtab_and_combine(epvector::iterator &first_numeric,
01323                                           epvector::iterator &last_non_zero,
01324                                           std::vector<bool> &touched,
01325                                           unsigned &number_of_zeroes)
01326 {
01327     epp current = seq.begin();
01328 
01329     while (current!=first_numeric) {
01330         if (is_exactly_a<numeric>(current->rest)) {
01331             --first_numeric;
01332             iter_swap(current,first_numeric);
01333         } else {
01334             // calculate hashindex
01335             unsigned currenthashindex = calc_hashindex(current->rest);
01336 
01337             // test if there is already a matching expair in the hashtab-list
01338             epplist &eppl=hashtab[currenthashindex];
01339             epplist::iterator epplit = eppl.begin();
01340             while (epplit!=eppl.end()) {
01341                 if (current->rest.is_equal((*epplit)->rest))
01342                     break;
01343                 ++epplit;
01344             }
01345             if (epplit==eppl.end()) {
01346                 // no matching expair found, append this to end of list
01347                 sorted_insert(eppl,current);
01348                 ++current;
01349             } else {
01350                 // epplit points to a matching expair, combine it with current
01351                 (*epplit)->coeff = ex_to<numeric>((*epplit)->coeff).
01352                                    add_dyn(ex_to<numeric>(current->coeff));
01353                 
01354                 // move obsolete current expair to end by swapping with last_non_zero element
01355                 // if this was a numeric, it is swapped with the expair before first_numeric 
01356                 iter_swap(current,last_non_zero);
01357                 --first_numeric;
01358                 if (first_numeric!=last_non_zero) iter_swap(first_numeric,current);
01359                 --last_non_zero;
01360                 ++number_of_zeroes;
01361                 // test if combined term has coeff 0 and can be removed is done later
01362                 touched[(*epplit)-seq.begin()] = true;
01363             }
01364         }
01365     }
01366 }
01367 
01368 void expairseq::drop_coeff_0_terms(epvector::iterator &first_numeric,
01369                                    epvector::iterator &last_non_zero,
01370                                    std::vector<bool> &touched,
01371                                    unsigned &number_of_zeroes)
01372 {
01373     // move terms with coeff 0 to end and remove them from hashtab
01374     // check only those elements which have been touched
01375     epp current = seq.begin();
01376     size_t i = 0;
01377     while (current!=first_numeric) {
01378         if (!touched[i]) {
01379             ++current;
01380             ++i;
01381         } else if (!ex_to<numeric>((*current).coeff).is_zero()) {
01382             ++current;
01383             ++i;
01384         } else {
01385             remove_hashtab_entry(current);
01386             
01387             // move element to the end, unless it is already at the end
01388             if (current!=last_non_zero) {
01389                 iter_swap(current,last_non_zero);
01390                 --first_numeric;
01391                 bool numeric_swapped = first_numeric!=last_non_zero;
01392                 if (numeric_swapped)
01393                     iter_swap(first_numeric,current);
01394                 epvector::iterator changed_entry;
01395 
01396                 if (numeric_swapped)
01397                     changed_entry = first_numeric;
01398                 else
01399                     changed_entry = last_non_zero;
01400                 
01401                 --last_non_zero;
01402                 ++number_of_zeroes;
01403 
01404                 if (first_numeric!=current) {
01405 
01406                     // change entry in hashtab which referred to first_numeric or last_non_zero to current
01407                     move_hashtab_entry(changed_entry,current);
01408                     touched[current-seq.begin()] = touched[changed_entry-seq.begin()];
01409                 }
01410             } else {
01411                 --first_numeric;
01412                 --last_non_zero;
01413                 ++number_of_zeroes;
01414             }
01415         }
01416     }
01417     GINAC_ASSERT(i==current-seq.begin());
01418 }
01419 
01423 bool expairseq::has_coeff_0() const
01424 {
01425     epvector::const_iterator i = seq.begin(), end = seq.end();
01426     while (i != end) {
01427         if (i->coeff.is_zero())
01428             return true;
01429         ++i;
01430     }
01431     return false;
01432 }
01433 
01434 void expairseq::add_numerics_to_hashtab(epvector::iterator first_numeric,
01435                                         epvector::const_iterator last_non_zero)
01436 {
01437     if (first_numeric == seq.end()) return; // no numerics
01438     
01439     epvector::const_iterator current = first_numeric, last = last_non_zero + 1;
01440     while (current != last) {
01441         sorted_insert(hashtab[hashmask], current);
01442         ++current;
01443     }
01444 }
01445 
01446 void expairseq::combine_same_terms()
01447 {
01448     // combine same terms, drop term with coeff 0, move numerics to end
01449     
01450     // calculate size of hashtab
01451     hashtabsize = calc_hashtabsize(seq.size());
01452     
01453     // hashtabsize is a power of 2
01454     hashmask = hashtabsize-1;
01455     
01456     // allocate hashtab
01457     hashtab.clear();
01458     hashtab.resize(hashtabsize);
01459     
01460     if (hashtabsize==0) {
01461         canonicalize();
01462         combine_same_terms_sorted_seq();
01463         GINAC_ASSERT(!has_coeff_0());
01464         return;
01465     }
01466     
01467     // iterate through seq, move numerics to end,
01468     // fill hashtab and combine same terms
01469     epvector::iterator first_numeric = seq.end();
01470     epvector::iterator last_non_zero = seq.end()-1;
01471     
01472     size_t num = seq.size();
01473     std::vector<bool> touched(num);
01474     
01475     unsigned number_of_zeroes = 0;
01476     
01477     GINAC_ASSERT(!has_coeff_0());
01478     build_hashtab_and_combine(first_numeric,last_non_zero,touched,number_of_zeroes);
01479     
01480     // there should not be any terms with coeff 0 from the beginning,
01481     // so it should be safe to skip this step
01482     if (number_of_zeroes!=0) {
01483         drop_coeff_0_terms(first_numeric,last_non_zero,touched,number_of_zeroes);
01484     }
01485     
01486     add_numerics_to_hashtab(first_numeric,last_non_zero);
01487     
01488     // pop zero elements
01489     for (unsigned i=0; i<number_of_zeroes; ++i) {
01490         seq.pop_back();
01491     }
01492     
01493     // shrink hashtabsize to calculated value
01494     GINAC_ASSERT(!has_coeff_0());
01495     
01496     shrink_hashtab();
01497     
01498     GINAC_ASSERT(!has_coeff_0());
01499 }
01500 
01501 #endif // EXPAIRSEQ_USE_HASHTAB
01502 
01505 bool expairseq::is_canonical() const
01506 {
01507     if (seq.size() <= 1)
01508         return 1;
01509     
01510 #if EXPAIRSEQ_USE_HASHTAB
01511     if (hashtabsize > 0) return 1; // not canoncalized
01512 #endif // EXPAIRSEQ_USE_HASHTAB
01513     
01514     epvector::const_iterator it = seq.begin(), itend = seq.end();
01515     epvector::const_iterator it_last = it;
01516     for (++it; it!=itend; it_last=it, ++it) {
01517         if (!(it_last->is_less(*it) || it_last->is_equal(*it))) {
01518             if (!is_exactly_a<numeric>(it_last->rest) ||
01519                 !is_exactly_a<numeric>(it->rest)) {
01520                 // double test makes it easier to set a breakpoint...
01521                 if (!is_exactly_a<numeric>(it_last->rest) ||
01522                     !is_exactly_a<numeric>(it->rest)) {
01523                     printpair(std::clog, *it_last, 0);
01524                     std::clog << ">";
01525                     printpair(std::clog, *it, 0);
01526                     std::clog << "\n";
01527                     std::clog << "pair1:" << std::endl;
01528                     it_last->rest.print(print_tree(std::clog));
01529                     it_last->coeff.print(print_tree(std::clog));
01530                     std::clog << "pair2:" << std::endl;
01531                     it->rest.print(print_tree(std::clog));
01532                     it->coeff.print(print_tree(std::clog));
01533                     return 0;
01534                 }
01535             }
01536         }
01537     }
01538     return 1;
01539 }
01540 
01541 
01547 std::auto_ptr<epvector> expairseq::expandchildren(unsigned options) const
01548 {
01549     const epvector::const_iterator last = seq.end();
01550     epvector::const_iterator cit = seq.begin();
01551     while (cit!=last) {
01552         const ex &expanded_ex = cit->rest.expand(options);
01553         if (!are_ex_trivially_equal(cit->rest,expanded_ex)) {
01554             
01555             // something changed, copy seq, eval and return it
01556             std::auto_ptr<epvector> s(new epvector);
01557             s->reserve(seq.size());
01558             
01559             // copy parts of seq which are known not to have changed
01560             epvector::const_iterator cit2 = seq.begin();
01561             while (cit2!=cit) {
01562                 s->push_back(*cit2);
01563                 ++cit2;
01564             }
01565 
01566             // copy first changed element
01567             s->push_back(combine_ex_with_coeff_to_pair(expanded_ex,
01568                                                        cit2->coeff));
01569             ++cit2;
01570 
01571             // copy rest
01572             while (cit2!=last) {
01573                 s->push_back(combine_ex_with_coeff_to_pair(cit2->rest.expand(options),
01574                                                            cit2->coeff));
01575                 ++cit2;
01576             }
01577             return s;
01578         }
01579         ++cit;
01580     }
01581     
01582     return std::auto_ptr<epvector>(0); // signalling nothing has changed
01583 }
01584 
01585 
01591 std::auto_ptr<epvector> expairseq::evalchildren(int level) const
01592 {
01593     // returns a NULL pointer if nothing had to be evaluated
01594     // returns a pointer to a newly created epvector otherwise
01595     // (which has to be deleted somewhere else)
01596 
01597     if (level==1)
01598         return std::auto_ptr<epvector>(0);
01599     
01600     if (level == -max_recursion_level)
01601         throw(std::runtime_error("max recursion level reached"));
01602     
01603     --level;
01604     epvector::const_iterator last = seq.end();
01605     epvector::const_iterator cit = seq.begin();
01606     while (cit!=last) {
01607         const ex &evaled_ex = cit->rest.eval(level);
01608         if (!are_ex_trivially_equal(cit->rest,evaled_ex)) {
01609             
01610             // something changed, copy seq, eval and return it
01611             std::auto_ptr<epvector> s(new epvector);
01612             s->reserve(seq.size());
01613             
01614             // copy parts of seq which are known not to have changed
01615             epvector::const_iterator cit2=seq.begin();
01616             while (cit2!=cit) {
01617                 s->push_back(*cit2);
01618                 ++cit2;
01619             }
01620 
01621             // copy first changed element
01622             s->push_back(combine_ex_with_coeff_to_pair(evaled_ex,
01623                                                        cit2->coeff));
01624             ++cit2;
01625 
01626             // copy rest
01627             while (cit2!=last) {
01628                 s->push_back(combine_ex_with_coeff_to_pair(cit2->rest.eval(level),
01629                                                            cit2->coeff));
01630                 ++cit2;
01631             }
01632             return s;
01633         }
01634         ++cit;
01635     }
01636     
01637     return std::auto_ptr<epvector>(0); // signalling nothing has changed
01638 }
01639 
01645 std::auto_ptr<epvector> expairseq::subschildren(const exmap & m, unsigned options) const
01646 {
01647     // When any of the objects to be substituted is a product or power
01648     // we have to recombine the pairs because the numeric coefficients may
01649     // be part of the search pattern.
01650     if (!(options & (subs_options::pattern_is_product | subs_options::pattern_is_not_product))) {
01651 
01652         // Search the list of substitutions and cache our findings
01653         for (exmap::const_iterator it = m.begin(); it != m.end(); ++it) {
01654             if (is_exactly_a<mul>(it->first) || is_exactly_a<power>(it->first)) {
01655                 options |= subs_options::pattern_is_product;
01656                 break;
01657             }
01658         }
01659         if (!(options & subs_options::pattern_is_product))
01660             options |= subs_options::pattern_is_not_product;
01661     }
01662 
01663     if (options & subs_options::pattern_is_product) {
01664 
01665         // Substitute in the recombined pairs
01666         epvector::const_iterator cit = seq.begin(), last = seq.end();
01667         while (cit != last) {
01668 
01669             const ex &orig_ex = recombine_pair_to_ex(*cit);
01670             const ex &subsed_ex = orig_ex.subs(m, options);
01671             if (!are_ex_trivially_equal(orig_ex, subsed_ex)) {
01672 
01673                 // Something changed, copy seq, subs and return it
01674                 std::auto_ptr<epvector> s(new epvector);
01675                 s->reserve(seq.size());
01676 
01677                 // Copy parts of seq which are known not to have changed
01678                 s->insert(s->begin(), seq.begin(), cit);
01679 
01680                 // Copy first changed element
01681                 s->push_back(split_ex_to_pair(subsed_ex));
01682                 ++cit;
01683 
01684                 // Copy rest
01685                 while (cit != last) {
01686                     s->push_back(split_ex_to_pair(recombine_pair_to_ex(*cit).subs(m, options)));
01687                     ++cit;
01688                 }
01689                 return s;
01690             }
01691 
01692             ++cit;
01693         }
01694 
01695     } else {
01696 
01697         // Substitute only in the "rest" part of the pairs
01698         epvector::const_iterator cit = seq.begin(), last = seq.end();
01699         while (cit != last) {
01700 
01701             const ex &subsed_ex = cit->rest.subs(m, options);
01702             if (!are_ex_trivially_equal(cit->rest, subsed_ex)) {
01703             
01704                 // Something changed, copy seq, subs and return it
01705                 std::auto_ptr<epvector> s(new epvector);
01706                 s->reserve(seq.size());
01707 
01708                 // Copy parts of seq which are known not to have changed
01709                 s->insert(s->begin(), seq.begin(), cit);
01710             
01711                 // Copy first changed element
01712                 s->push_back(combine_ex_with_coeff_to_pair(subsed_ex, cit->coeff));
01713                 ++cit;
01714 
01715                 // Copy rest
01716                 while (cit != last) {
01717                     s->push_back(combine_ex_with_coeff_to_pair(cit->rest.subs(m, options), cit->coeff));
01718                     ++cit;
01719                 }
01720                 return s;
01721             }
01722 
01723             ++cit;
01724         }
01725     }
01726     
01727     // Nothing has changed
01728     return std::auto_ptr<epvector>(0);
01729 }
01730 
01732 // static member variables
01734 
01735 #if EXPAIRSEQ_USE_HASHTAB
01736 unsigned expairseq::maxhashtabsize = 0x4000000U;
01737 unsigned expairseq::minhashtabsize = 0x1000U;
01738 unsigned expairseq::hashtabfactor = 1;
01739 #endif // EXPAIRSEQ_USE_HASHTAB
01740 
01741 } // namespace GiNaC

This page is part of the GiNaC developer's reference. It was generated automatically by doxygen. For an introduction, see the tutorial.