- New tinfo mechanism
[ginac.git] / ginac / expairseq.cpp
1 /** @file expairseq.cpp
2  *
3  *  Implementation of sequences of expression pairs. */
4
5 /*
6  *  GiNaC Copyright (C) 1999-2006 Johannes Gutenberg University Mainz, Germany
7  *
8  *  This program is free software; you can redistribute it and/or modify
9  *  it under the terms of the GNU General Public License as published by
10  *  the Free Software Foundation; either version 2 of the License, or
11  *  (at your option) any later version.
12  *
13  *  This program is distributed in the hope that it will be useful,
14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  *  GNU General Public License for more details.
17  *
18  *  You should have received a copy of the GNU General Public License
19  *  along with this program; if not, write to the Free Software
20  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
21  */
22
23 #include <iostream>
24 #include <algorithm>
25 #include <string>
26 #include <stdexcept>
27
28 #include "expairseq.h"
29 #include "lst.h"
30 #include "mul.h"
31 #include "power.h"
32 #include "relational.h"
33 #include "wildcard.h"
34 #include "archive.h"
35 #include "operators.h"
36 #include "utils.h"
37 #include "indexed.h"
38
39 #if EXPAIRSEQ_USE_HASHTAB
40 #include <cmath>
41 #endif // EXPAIRSEQ_USE_HASHTAB
42
43 namespace GiNaC {
44
45         
46 GINAC_IMPLEMENT_REGISTERED_CLASS_OPT(expairseq, basic,
47   print_func<print_context>(&expairseq::do_print).
48   print_func<print_tree>(&expairseq::do_print_tree))
49
50
51 //////////
52 // helper classes
53 //////////
54
55 class epp_is_less
56 {
57 public:
58         bool operator()(const epp &lh, const epp &rh) const
59         {
60                 return (*lh).is_less(*rh);
61         }
62 };
63
64 //////////
65 // default constructor
66 //////////
67
68 // public
69
70 expairseq::expairseq() : inherited(&expairseq::tinfo_static)
71 #if EXPAIRSEQ_USE_HASHTAB
72                                                    , hashtabsize(0)
73 #endif // EXPAIRSEQ_USE_HASHTAB
74 {}
75
76 // protected
77
78 #if 0
79 /** For use by copy ctor and assignment operator. */
80 void expairseq::copy(const expairseq &other)
81 {
82         seq = other.seq;
83         overall_coeff = other.overall_coeff;
84 #if EXPAIRSEQ_USE_HASHTAB
85         // copy hashtab
86         hashtabsize = other.hashtabsize;
87         if (hashtabsize!=0) {
88                 hashmask = other.hashmask;
89                 hashtab.resize(hashtabsize);
90                 epvector::const_iterator osb = other.seq.begin();
91                 for (unsigned i=0; i<hashtabsize; ++i) {
92                         hashtab[i].clear();
93                         for (epplist::const_iterator cit=other.hashtab[i].begin();
94                              cit!=other.hashtab[i].end(); ++cit) {
95                                 hashtab[i].push_back(seq.begin()+((*cit)-osb));
96                         }
97                 }
98         } else {
99                 hashtab.clear();
100         }
101 #endif // EXPAIRSEQ_USE_HASHTAB
102 }
103 #endif
104
105 //////////
106 // other constructors
107 //////////
108
109 expairseq::expairseq(const ex &lh, const ex &rh) : inherited(&expairseq::tinfo_static)
110 {
111         construct_from_2_ex(lh,rh);
112         GINAC_ASSERT(is_canonical());
113 }
114
115 expairseq::expairseq(const exvector &v) : inherited(&expairseq::tinfo_static)
116 {
117         construct_from_exvector(v);
118         GINAC_ASSERT(is_canonical());
119 }
120
121 expairseq::expairseq(const epvector &v, const ex &oc)
122   : inherited(&expairseq::tinfo_static), overall_coeff(oc)
123 {
124         GINAC_ASSERT(is_a<numeric>(oc));
125         construct_from_epvector(v);
126         GINAC_ASSERT(is_canonical());
127 }
128
129 expairseq::expairseq(std::auto_ptr<epvector> vp, const ex &oc)
130   : inherited(&expairseq::tinfo_static), overall_coeff(oc)
131 {
132         GINAC_ASSERT(vp.get()!=0);
133         GINAC_ASSERT(is_a<numeric>(oc));
134         construct_from_epvector(*vp);
135         GINAC_ASSERT(is_canonical());
136 }
137
138 //////////
139 // archiving
140 //////////
141
142 expairseq::expairseq(const archive_node &n, lst &sym_lst) : inherited(n, sym_lst)
143 #if EXPAIRSEQ_USE_HASHTAB
144         , hashtabsize(0)
145 #endif
146 {
147         for (unsigned int i=0; true; i++) {
148                 ex rest;
149                 ex coeff;
150                 if (n.find_ex("rest", rest, sym_lst, i) && n.find_ex("coeff", coeff, sym_lst, i))
151                         seq.push_back(expair(rest, coeff));
152                 else
153                         break;
154         }
155
156         n.find_ex("overall_coeff", overall_coeff, sym_lst);
157
158         canonicalize();
159         GINAC_ASSERT(is_canonical());
160 }
161
162 void expairseq::archive(archive_node &n) const
163 {
164         inherited::archive(n);
165         epvector::const_iterator i = seq.begin(), iend = seq.end();
166         while (i != iend) {
167                 n.add_ex("rest", i->rest);
168                 n.add_ex("coeff", i->coeff);
169                 ++i;
170         }
171         n.add_ex("overall_coeff", overall_coeff);
172 }
173
174 DEFAULT_UNARCHIVE(expairseq)
175
176 //////////
177 // functions overriding virtual functions from base classes
178 //////////
179
180 // public
181
182 void expairseq::do_print(const print_context & c, unsigned level) const
183 {
184         c.s << "[[";
185         printseq(c, ',', precedence(), level);
186         c.s << "]]";
187 }
188
189 void expairseq::do_print_tree(const print_tree & c, unsigned level) const
190 {
191         c.s << std::string(level, ' ') << class_name() << " @" << this
192             << std::hex << ", hash=0x" << hashvalue << ", flags=0x" << flags << std::dec
193             << ", nops=" << nops()
194             << std::endl;
195         size_t num = seq.size();
196         for (size_t i=0; i<num; ++i) {
197                 seq[i].rest.print(c, level + c.delta_indent);
198                 seq[i].coeff.print(c, level + c.delta_indent);
199                 if (i != num - 1)
200                         c.s << std::string(level + c.delta_indent, ' ') << "-----" << std::endl;
201         }
202         if (!overall_coeff.is_equal(default_overall_coeff())) {
203                 c.s << std::string(level + c.delta_indent, ' ') << "-----" << std::endl
204                     << std::string(level + c.delta_indent, ' ') << "overall_coeff" << std::endl;
205                 overall_coeff.print(c, level + c.delta_indent);
206         }
207         c.s << std::string(level + c.delta_indent,' ') << "=====" << std::endl;
208 #if EXPAIRSEQ_USE_HASHTAB
209         c.s << std::string(level + c.delta_indent,' ')
210             << "hashtab size " << hashtabsize << std::endl;
211         if (hashtabsize == 0) return;
212 #define MAXCOUNT 5
213         unsigned count[MAXCOUNT+1];
214         for (int i=0; i<MAXCOUNT+1; ++i)
215                 count[i] = 0;
216         unsigned this_bin_fill;
217         unsigned cum_fill_sq = 0;
218         unsigned cum_fill = 0;
219         for (unsigned i=0; i<hashtabsize; ++i) {
220                 this_bin_fill = 0;
221                 if (hashtab[i].size() > 0) {
222                         c.s << std::string(level + c.delta_indent, ' ')
223                             << "bin " << i << " with entries ";
224                         for (epplist::const_iterator it=hashtab[i].begin();
225                              it!=hashtab[i].end(); ++it) {
226                                 c.s << *it-seq.begin() << " ";
227                                 ++this_bin_fill;
228                         }
229                         c.s << std::endl;
230                         cum_fill += this_bin_fill;
231                         cum_fill_sq += this_bin_fill*this_bin_fill;
232                 }
233                 if (this_bin_fill<MAXCOUNT)
234                         ++count[this_bin_fill];
235                 else
236                         ++count[MAXCOUNT];
237         }
238         unsigned fact = 1;
239         double cum_prob = 0;
240         double lambda = (1.0*seq.size()) / hashtabsize;
241         for (int k=0; k<MAXCOUNT; ++k) {
242                 if (k>0)
243                         fact *= k;
244                 double prob = std::pow(lambda,k)/fact * std::exp(-lambda);
245                 cum_prob += prob;
246                 c.s << std::string(level + c.delta_indent, ' ') << "bins with " << k << " entries: "
247                     << int(1000.0*count[k]/hashtabsize)/10.0 << "% (expected: "
248                     << int(prob*1000)/10.0 << ")" << std::endl;
249         }
250         c.s << std::string(level + c.delta_indent, ' ') << "bins with more entries: "
251             << int(1000.0*count[MAXCOUNT]/hashtabsize)/10.0 << "% (expected: "
252             << int((1-cum_prob)*1000)/10.0 << ")" << std::endl;
253
254         c.s << std::string(level + c.delta_indent, ' ') << "variance: "
255             << 1.0/hashtabsize*cum_fill_sq-(1.0/hashtabsize*cum_fill)*(1.0/hashtabsize*cum_fill)
256             << std::endl;
257         c.s << std::string(level + c.delta_indent, ' ') << "average fill: "
258             << (1.0*cum_fill)/hashtabsize
259             << " (should be equal to " << (1.0*seq.size())/hashtabsize << ")" << std::endl;
260 #endif // EXPAIRSEQ_USE_HASHTAB
261 }
262
263 bool expairseq::info(unsigned inf) const
264 {
265         return inherited::info(inf);
266 }
267
268 size_t expairseq::nops() const
269 {
270         if (overall_coeff.is_equal(default_overall_coeff()))
271                 return seq.size();
272         else
273                 return seq.size()+1;
274 }
275
276 ex expairseq::op(size_t i) const
277 {
278         if (i < seq.size())
279                 return recombine_pair_to_ex(seq[i]);
280         GINAC_ASSERT(!overall_coeff.is_equal(default_overall_coeff()));
281         return overall_coeff;
282 }
283
284 ex expairseq::map(map_function &f) const
285 {
286         std::auto_ptr<epvector> v(new epvector);
287         v->reserve(seq.size());
288
289         epvector::const_iterator cit = seq.begin(), last = seq.end();
290         while (cit != last) {
291                 v->push_back(split_ex_to_pair(f(recombine_pair_to_ex(*cit))));
292                 ++cit;
293         }
294
295         if (overall_coeff.is_equal(default_overall_coeff()))
296                 return thisexpairseq(v, default_overall_coeff());
297         else
298                 return thisexpairseq(v, f(overall_coeff));
299 }
300
301 /** Perform coefficient-wise automatic term rewriting rules in this class. */
302 ex expairseq::eval(int level) const
303 {
304         if ((level==1) && (flags &status_flags::evaluated))
305                 return *this;
306         
307         std::auto_ptr<epvector> vp = evalchildren(level);
308         if (vp.get() == 0)
309                 return this->hold();
310         
311         return (new expairseq(vp, overall_coeff))->setflag(status_flags::dynallocated | status_flags::evaluated);
312 }
313
314 epvector* conjugateepvector(const epvector&epv)
315 {
316         epvector *newepv = 0;
317         for (epvector::const_iterator i=epv.begin(); i!=epv.end(); ++i) {
318                 if(newepv) {
319                         newepv->push_back(i->conjugate());
320                         continue;
321                 }
322                 expair x = i->conjugate();
323                 if (x.is_equal(*i)) {
324                         continue;
325                 }
326                 newepv = new epvector;
327                 newepv->reserve(epv.size());
328                 for (epvector::const_iterator j=epv.begin(); j!=i; ++j) {
329                         newepv->push_back(*j);
330                 }
331                 newepv->push_back(x);
332         }
333         return newepv;
334 }
335
336 ex expairseq::conjugate() const
337 {
338         epvector* newepv = conjugateepvector(seq);
339         ex x = overall_coeff.conjugate();
340         if (!newepv && are_ex_trivially_equal(x, overall_coeff)) {
341                 return *this;
342         }
343         ex result = thisexpairseq(newepv ? *newepv : seq, x);
344         if (newepv) {
345                 delete newepv;
346         }
347         return result;
348 }
349
350 bool expairseq::match(const ex & pattern, lst & repl_lst) const
351 {
352         // This differs from basic::match() because we want "a+b+c+d" to
353         // match "d+*+b" with "*" being "a+c", and we want to honor commutativity
354
355         if (this->tinfo() == ex_to<basic>(pattern).tinfo()) {
356
357                 // Check whether global wildcard (one that matches the "rest of the
358                 // expression", like "*" above) is present
359                 bool has_global_wildcard = false;
360                 ex global_wildcard;
361                 for (size_t i=0; i<pattern.nops(); i++) {
362                         if (is_exactly_a<wildcard>(pattern.op(i))) {
363                                 has_global_wildcard = true;
364                                 global_wildcard = pattern.op(i);
365                                 break;
366                         }
367                 }
368
369                 // Unfortunately, this is an O(N^2) operation because we can't
370                 // sort the pattern in a useful way...
371
372                 // Chop into terms
373                 exvector ops;
374                 ops.reserve(nops());
375                 for (size_t i=0; i<nops(); i++)
376                         ops.push_back(op(i));
377
378                 // Now, for every term of the pattern, look for a matching term in
379                 // the expression and remove the match
380                 for (size_t i=0; i<pattern.nops(); i++) {
381                         ex p = pattern.op(i);
382                         if (has_global_wildcard && p.is_equal(global_wildcard))
383                                 continue;
384                         exvector::iterator it = ops.begin(), itend = ops.end();
385                         while (it != itend) {
386                                 if (it->match(p, repl_lst)) {
387                                         ops.erase(it);
388                                         goto found;
389                                 }
390                                 ++it;
391                         }
392                         return false; // no match found
393 found:          ;
394                 }
395
396                 if (has_global_wildcard) {
397
398                         // Assign all the remaining terms to the global wildcard (unless
399                         // it has already been matched before, in which case the matches
400                         // must be equal)
401                         size_t num = ops.size();
402                         std::auto_ptr<epvector> vp(new epvector);
403                         vp->reserve(num);
404                         for (size_t i=0; i<num; i++)
405                                 vp->push_back(split_ex_to_pair(ops[i]));
406                         ex rest = thisexpairseq(vp, default_overall_coeff());
407                         for (lst::const_iterator it = repl_lst.begin(); it != repl_lst.end(); ++it) {
408                                 if (it->op(0).is_equal(global_wildcard))
409                                         return rest.is_equal(it->op(1));
410                         }
411                         repl_lst.append(global_wildcard == rest);
412                         return true;
413
414                 } else {
415
416                         // No global wildcard, then the match fails if there are any
417                         // unmatched terms left
418                         return ops.empty();
419                 }
420         }
421         return inherited::match(pattern, repl_lst);
422 }
423
424 ex expairseq::subs(const exmap & m, unsigned options) const
425 {
426         std::auto_ptr<epvector> vp = subschildren(m, options);
427         if (vp.get())
428                 return ex_to<basic>(thisexpairseq(vp, overall_coeff));
429         else if ((options & subs_options::algebraic) && is_exactly_a<mul>(*this))
430                 return static_cast<const mul *>(this)->algebraic_subs_mul(m, options);
431         else
432                 return subs_one_level(m, options);
433 }
434
435 // protected
436
437 int expairseq::compare_same_type(const basic &other) const
438 {
439         GINAC_ASSERT(is_a<expairseq>(other));
440         const expairseq &o = static_cast<const expairseq &>(other);
441         
442         int cmpval;
443         
444         // compare number of elements
445         if (seq.size() != o.seq.size())
446                 return (seq.size()<o.seq.size()) ? -1 : 1;
447         
448         // compare overall_coeff
449         cmpval = overall_coeff.compare(o.overall_coeff);
450         if (cmpval!=0)
451                 return cmpval;
452         
453 #if EXPAIRSEQ_USE_HASHTAB
454         GINAC_ASSERT(hashtabsize==o.hashtabsize);
455         if (hashtabsize==0) {
456 #endif // EXPAIRSEQ_USE_HASHTAB
457                 epvector::const_iterator cit1 = seq.begin();
458                 epvector::const_iterator cit2 = o.seq.begin();
459                 epvector::const_iterator last1 = seq.end();
460                 epvector::const_iterator last2 = o.seq.end();
461                 
462                 for (; (cit1!=last1)&&(cit2!=last2); ++cit1, ++cit2) {
463                         cmpval = (*cit1).compare(*cit2);
464                         if (cmpval!=0) return cmpval;
465                 }
466                 
467                 GINAC_ASSERT(cit1==last1);
468                 GINAC_ASSERT(cit2==last2);
469                 
470                 return 0;
471 #if EXPAIRSEQ_USE_HASHTAB
472         }
473         
474         // compare number of elements in each hashtab entry
475         for (unsigned i=0; i<hashtabsize; ++i) {
476                 unsigned cursize=hashtab[i].size();
477                 if (cursize != o.hashtab[i].size())
478                         return (cursize < o.hashtab[i].size()) ? -1 : 1;
479         }
480         
481         // compare individual (sorted) hashtab entries
482         for (unsigned i=0; i<hashtabsize; ++i) {
483                 unsigned sz = hashtab[i].size();
484                 if (sz>0) {
485                         const epplist &eppl1 = hashtab[i];
486                         const epplist &eppl2 = o.hashtab[i];
487                         epplist::const_iterator it1 = eppl1.begin();
488                         epplist::const_iterator it2 = eppl2.begin();
489                         while (it1!=eppl1.end()) {
490                                 cmpval = (*(*it1)).compare(*(*it2));
491                                 if (cmpval!=0)
492                                         return cmpval;
493                                 ++it1;
494                                 ++it2;
495                         }
496                 }
497         }
498         
499         return 0; // equal
500 #endif // EXPAIRSEQ_USE_HASHTAB
501 }
502
503 bool expairseq::is_equal_same_type(const basic &other) const
504 {
505         const expairseq &o = static_cast<const expairseq &>(other);
506         
507         // compare number of elements
508         if (seq.size()!=o.seq.size())
509                 return false;
510         
511         // compare overall_coeff
512         if (!overall_coeff.is_equal(o.overall_coeff))
513                 return false;
514         
515 #if EXPAIRSEQ_USE_HASHTAB
516         // compare number of elements in each hashtab entry
517         if (hashtabsize!=o.hashtabsize) {
518                 std::cout << "this:" << std::endl;
519                 print(print_tree(std::cout));
520                 std::cout << "other:" << std::endl;
521                 other.print(print_tree(std::cout));
522         }
523                 
524         GINAC_ASSERT(hashtabsize==o.hashtabsize);
525         
526         if (hashtabsize==0) {
527 #endif // EXPAIRSEQ_USE_HASHTAB
528                 epvector::const_iterator cit1 = seq.begin();
529                 epvector::const_iterator cit2 = o.seq.begin();
530                 epvector::const_iterator last1 = seq.end();
531                 
532                 while (cit1!=last1) {
533                         if (!(*cit1).is_equal(*cit2)) return false;
534                         ++cit1;
535                         ++cit2;
536                 }
537                 
538                 return true;
539 #if EXPAIRSEQ_USE_HASHTAB
540         }
541         
542         for (unsigned i=0; i<hashtabsize; ++i) {
543                 if (hashtab[i].size() != o.hashtab[i].size())
544                         return false;
545         }
546
547         // compare individual sorted hashtab entries
548         for (unsigned i=0; i<hashtabsize; ++i) {
549                 unsigned sz = hashtab[i].size();
550                 if (sz>0) {
551                         const epplist &eppl1 = hashtab[i];
552                         const epplist &eppl2 = o.hashtab[i];
553                         epplist::const_iterator it1 = eppl1.begin();
554                         epplist::const_iterator it2 = eppl2.begin();
555                         while (it1!=eppl1.end()) {
556                                 if (!(*(*it1)).is_equal(*(*it2))) return false;
557                                 ++it1;
558                                 ++it2;
559                         }
560                 }
561         }
562         
563         return true;
564 #endif // EXPAIRSEQ_USE_HASHTAB
565 }
566
567 unsigned expairseq::return_type() const
568 {
569         return return_types::noncommutative_composite;
570 }
571
572 unsigned expairseq::calchash() const
573 {
574         unsigned v = golden_ratio_hash((unsigned)this->tinfo());
575         epvector::const_iterator i = seq.begin();
576         const epvector::const_iterator end = seq.end();
577         while (i != end) {
578                 v ^= i->rest.gethash();
579 #if !EXPAIRSEQ_USE_HASHTAB
580                 // rotation spoils commutativity!
581                 v = rotate_left(v);
582                 v ^= i->coeff.gethash();
583 #endif // !EXPAIRSEQ_USE_HASHTAB
584                 ++i;
585         }
586
587         v ^= overall_coeff.gethash();
588
589         // store calculated hash value only if object is already evaluated
590         if (flags &status_flags::evaluated) {
591                 setflag(status_flags::hash_calculated);
592                 hashvalue = v;
593         }
594         
595         return v;
596 }
597
598 ex expairseq::expand(unsigned options) const
599 {
600         std::auto_ptr<epvector> vp = expandchildren(options);
601         if (vp.get())
602                 return thisexpairseq(vp, overall_coeff);
603         else {
604                 // The terms have not changed, so it is safe to declare this expanded
605                 return (options == 0) ? setflag(status_flags::expanded) : *this;
606         }
607 }
608
609 //////////
610 // new virtual functions which can be overridden by derived classes
611 //////////
612
613 // protected
614
615 /** Create an object of this type.
616  *  This method works similar to a constructor.  It is useful because expairseq
617  *  has (at least) two possible different semantics but we want to inherit
618  *  methods thus avoiding code duplication.  Sometimes a method in expairseq
619  *  has to create a new one of the same semantics, which cannot be done by a
620  *  ctor because the name (add, mul,...) is unknown on the expaiseq level.  In
621  *  order for this trick to work a derived class must of course override this
622  *  definition. */
623 ex expairseq::thisexpairseq(const epvector &v, const ex &oc) const
624 {
625         return expairseq(v, oc);
626 }
627
628 ex expairseq::thisexpairseq(std::auto_ptr<epvector> vp, const ex &oc) const
629 {
630         return expairseq(vp, oc);
631 }
632
633 void expairseq::printpair(const print_context & c, const expair & p, unsigned upper_precedence) const
634 {
635         c.s << "[[";
636         p.rest.print(c, precedence());
637         c.s << ",";
638         p.coeff.print(c, precedence());
639         c.s << "]]";
640 }
641
642 void expairseq::printseq(const print_context & c, char delim,
643                          unsigned this_precedence,
644                          unsigned upper_precedence) const
645 {
646         if (this_precedence <= upper_precedence)
647                 c.s << "(";
648         epvector::const_iterator it, it_last = seq.end() - 1;
649         for (it=seq.begin(); it!=it_last; ++it) {
650                 printpair(c, *it, this_precedence);
651                 c.s << delim;
652         }
653         printpair(c, *it, this_precedence);
654         if (!overall_coeff.is_equal(default_overall_coeff())) {
655                 c.s << delim;
656                 overall_coeff.print(c, this_precedence);
657         }
658         
659         if (this_precedence <= upper_precedence)
660                 c.s << ")";
661 }
662
663
664 /** Form an expair from an ex, using the corresponding semantics.
665  *  @see expairseq::recombine_pair_to_ex() */
666 expair expairseq::split_ex_to_pair(const ex &e) const
667 {
668         return expair(e,_ex1);
669 }
670
671
672 expair expairseq::combine_ex_with_coeff_to_pair(const ex &e,
673                                                 const ex &c) const
674 {
675         GINAC_ASSERT(is_exactly_a<numeric>(c));
676         
677         return expair(e,c);
678 }
679
680
681 expair expairseq::combine_pair_with_coeff_to_pair(const expair &p,
682                                                   const ex &c) const
683 {
684         GINAC_ASSERT(is_exactly_a<numeric>(p.coeff));
685         GINAC_ASSERT(is_exactly_a<numeric>(c));
686         
687         return expair(p.rest,ex_to<numeric>(p.coeff).mul_dyn(ex_to<numeric>(c)));
688 }
689
690
691 /** Form an ex out of an expair, using the corresponding semantics.
692  *  @see expairseq::split_ex_to_pair() */
693 ex expairseq::recombine_pair_to_ex(const expair &p) const
694 {
695         return lst(p.rest,p.coeff);
696 }
697
698 bool expairseq::expair_needs_further_processing(epp it)
699 {
700 #if EXPAIRSEQ_USE_HASHTAB
701         //#  error "FIXME: expair_needs_further_processing not yet implemented for hashtabs, sorry. A.F."
702 #endif // EXPAIRSEQ_USE_HASHTAB
703         return false;
704 }
705
706 ex expairseq::default_overall_coeff() const
707 {
708         return _ex0;
709 }
710
711 void expairseq::combine_overall_coeff(const ex &c)
712 {
713         GINAC_ASSERT(is_exactly_a<numeric>(overall_coeff));
714         GINAC_ASSERT(is_exactly_a<numeric>(c));
715         overall_coeff = ex_to<numeric>(overall_coeff).add_dyn(ex_to<numeric>(c));
716 }
717
718 void expairseq::combine_overall_coeff(const ex &c1, const ex &c2)
719 {
720         GINAC_ASSERT(is_exactly_a<numeric>(overall_coeff));
721         GINAC_ASSERT(is_exactly_a<numeric>(c1));
722         GINAC_ASSERT(is_exactly_a<numeric>(c2));
723         overall_coeff = ex_to<numeric>(overall_coeff).
724                         add_dyn(ex_to<numeric>(c1).mul(ex_to<numeric>(c2)));
725 }
726
727 bool expairseq::can_make_flat(const expair &p) const
728 {
729         return true;
730 }
731
732
733 //////////
734 // non-virtual functions in this class
735 //////////
736
737 void expairseq::construct_from_2_ex_via_exvector(const ex &lh, const ex &rh)
738 {
739         exvector v;
740         v.reserve(2);
741         v.push_back(lh);
742         v.push_back(rh);
743         construct_from_exvector(v);
744 #if EXPAIRSEQ_USE_HASHTAB
745         GINAC_ASSERT((hashtabsize==0)||(hashtabsize>=minhashtabsize));
746         GINAC_ASSERT(hashtabsize==calc_hashtabsize(seq.size()));
747 #endif // EXPAIRSEQ_USE_HASHTAB
748 }
749
750 void expairseq::construct_from_2_ex(const ex &lh, const ex &rh)
751 {
752         if (ex_to<basic>(lh).tinfo()==this->tinfo()) {
753                 if (ex_to<basic>(rh).tinfo()==this->tinfo()) {
754 #if EXPAIRSEQ_USE_HASHTAB
755                         unsigned totalsize = ex_to<expairseq>(lh).seq.size() +
756                                              ex_to<expairseq>(rh).seq.size();
757                         if (calc_hashtabsize(totalsize)!=0) {
758                                 construct_from_2_ex_via_exvector(lh,rh);
759                         } else {
760 #endif // EXPAIRSEQ_USE_HASHTAB
761                                 if(is_a<mul>(lh))
762                                 {       
763                                         ex newrh=rename_dummy_indices_uniquely(lh, rh);
764                                         construct_from_2_expairseq(ex_to<expairseq>(lh),
765                                                                    ex_to<expairseq>(newrh));
766                                 }
767                                 else
768                                         construct_from_2_expairseq(ex_to<expairseq>(lh),
769                                                                    ex_to<expairseq>(rh));
770 #if EXPAIRSEQ_USE_HASHTAB
771                         }
772 #endif // EXPAIRSEQ_USE_HASHTAB
773                         return;
774                 } else {
775 #if EXPAIRSEQ_USE_HASHTAB
776                         unsigned totalsize = ex_to<expairseq>(lh).seq.size()+1;
777                         if (calc_hashtabsize(totalsize)!=0) {
778                                 construct_from_2_ex_via_exvector(lh, rh);
779                         } else {
780 #endif // EXPAIRSEQ_USE_HASHTAB
781                                 construct_from_expairseq_ex(ex_to<expairseq>(lh), rh);
782 #if EXPAIRSEQ_USE_HASHTAB
783                         }
784 #endif // EXPAIRSEQ_USE_HASHTAB
785                         return;
786                 }
787         } else if (ex_to<basic>(rh).tinfo()==this->tinfo()) {
788 #if EXPAIRSEQ_USE_HASHTAB
789                 unsigned totalsize=ex_to<expairseq>(rh).seq.size()+1;
790                 if (calc_hashtabsize(totalsize)!=0) {
791                         construct_from_2_ex_via_exvector(lh,rh);
792                 } else {
793 #endif // EXPAIRSEQ_USE_HASHTAB
794                         construct_from_expairseq_ex(ex_to<expairseq>(rh),lh);
795 #if EXPAIRSEQ_USE_HASHTAB
796                 }
797 #endif // EXPAIRSEQ_USE_HASHTAB
798                 return;
799         }
800         
801 #if EXPAIRSEQ_USE_HASHTAB
802         if (calc_hashtabsize(2)!=0) {
803                 construct_from_2_ex_via_exvector(lh,rh);
804                 return;
805         }
806         hashtabsize = 0;
807 #endif // EXPAIRSEQ_USE_HASHTAB
808         
809         if (is_exactly_a<numeric>(lh)) {
810                 if (is_exactly_a<numeric>(rh)) {
811                         combine_overall_coeff(lh);
812                         combine_overall_coeff(rh);
813                 } else {
814                         combine_overall_coeff(lh);
815                         seq.push_back(split_ex_to_pair(rh));
816                 }
817         } else {
818                 if (is_exactly_a<numeric>(rh)) {
819                         combine_overall_coeff(rh);
820                         seq.push_back(split_ex_to_pair(lh));
821                 } else {
822                         expair p1 = split_ex_to_pair(lh);
823                         expair p2 = split_ex_to_pair(rh);
824                         
825                         int cmpval = p1.rest.compare(p2.rest);
826                         if (cmpval==0) {
827                                 p1.coeff = ex_to<numeric>(p1.coeff).add_dyn(ex_to<numeric>(p2.coeff));
828                                 if (!ex_to<numeric>(p1.coeff).is_zero()) {
829                                         // no further processing is necessary, since this
830                                         // one element will usually be recombined in eval()
831                                         seq.push_back(p1);
832                                 }
833                         } else {
834                                 seq.reserve(2);
835                                 if (cmpval<0) {
836                                         seq.push_back(p1);
837                                         seq.push_back(p2);
838                                 } else {
839                                         seq.push_back(p2);
840                                         seq.push_back(p1);
841                                 }
842                         }
843                 }
844         }
845 }
846
847 void expairseq::construct_from_2_expairseq(const expairseq &s1,
848                                                                                    const expairseq &s2)
849 {
850         combine_overall_coeff(s1.overall_coeff);
851         combine_overall_coeff(s2.overall_coeff);
852
853         epvector::const_iterator first1 = s1.seq.begin();
854         epvector::const_iterator last1 = s1.seq.end();
855         epvector::const_iterator first2 = s2.seq.begin();
856         epvector::const_iterator last2 = s2.seq.end();
857
858         seq.reserve(s1.seq.size()+s2.seq.size());
859
860         bool needs_further_processing=false;
861         
862         while (first1!=last1 && first2!=last2) {
863                 int cmpval = (*first1).rest.compare((*first2).rest);
864
865                 if (cmpval==0) {
866                         // combine terms
867                         const numeric &newcoeff = ex_to<numeric>(first1->coeff).
868                                                    add(ex_to<numeric>(first2->coeff));
869                         if (!newcoeff.is_zero()) {
870                                 seq.push_back(expair(first1->rest,newcoeff));
871                                 if (expair_needs_further_processing(seq.end()-1)) {
872                                         needs_further_processing = true;
873                                 }
874                         }
875                         ++first1;
876                         ++first2;
877                 } else if (cmpval<0) {
878                         seq.push_back(*first1);
879                         ++first1;
880                 } else {
881                         seq.push_back(*first2);
882                         ++first2;
883                 }
884         }
885         
886         while (first1!=last1) {
887                 seq.push_back(*first1);
888                 ++first1;
889         }
890         while (first2!=last2) {
891                 seq.push_back(*first2);
892                 ++first2;
893         }
894         
895         if (needs_further_processing) {
896                 epvector v = seq;
897                 seq.clear();
898                 construct_from_epvector(v);
899         }
900 }
901
902 void expairseq::construct_from_expairseq_ex(const expairseq &s,
903                                                                                         const ex &e)
904 {
905         combine_overall_coeff(s.overall_coeff);
906         if (is_exactly_a<numeric>(e)) {
907                 combine_overall_coeff(e);
908                 seq = s.seq;
909                 return;
910         }
911         
912         epvector::const_iterator first = s.seq.begin();
913         epvector::const_iterator last = s.seq.end();
914         expair p = split_ex_to_pair(e);
915         
916         seq.reserve(s.seq.size()+1);
917         bool p_pushed = false;
918         
919         bool needs_further_processing=false;
920         
921         // merge p into s.seq
922         while (first!=last) {
923                 int cmpval = (*first).rest.compare(p.rest);
924                 if (cmpval==0) {
925                         // combine terms
926                         const numeric &newcoeff = ex_to<numeric>(first->coeff).
927                                                    add(ex_to<numeric>(p.coeff));
928                         if (!newcoeff.is_zero()) {
929                                 seq.push_back(expair(first->rest,newcoeff));
930                                 if (expair_needs_further_processing(seq.end()-1))
931                                         needs_further_processing = true;
932                         }
933                         ++first;
934                         p_pushed = true;
935                         break;
936                 } else if (cmpval<0) {
937                         seq.push_back(*first);
938                         ++first;
939                 } else {
940                         seq.push_back(p);
941                         p_pushed = true;
942                         break;
943                 }
944         }
945         
946         if (p_pushed) {
947                 // while loop exited because p was pushed, now push rest of s.seq
948                 while (first!=last) {
949                         seq.push_back(*first);
950                         ++first;
951                 }
952         } else {
953                 // while loop exited because s.seq was pushed, now push p
954                 seq.push_back(p);
955         }
956
957         if (needs_further_processing) {
958                 epvector v = seq;
959                 seq.clear();
960                 construct_from_epvector(v);
961         }
962 }
963
964 void expairseq::construct_from_exvector(const exvector &v)
965 {
966         // simplifications: +(a,+(b,c),d) -> +(a,b,c,d) (associativity)
967         //                  +(d,b,c,a) -> +(a,b,c,d) (canonicalization)
968         //                  +(...,x,*(x,c1),*(x,c2)) -> +(...,*(x,1+c1+c2)) (c1, c2 numeric())
969         //                  (same for (+,*) -> (*,^)
970
971         make_flat(v);
972 #if EXPAIRSEQ_USE_HASHTAB
973         combine_same_terms();
974 #else
975         canonicalize();
976         combine_same_terms_sorted_seq();
977 #endif // EXPAIRSEQ_USE_HASHTAB
978 }
979
980 void expairseq::construct_from_epvector(const epvector &v)
981 {
982         // simplifications: +(a,+(b,c),d) -> +(a,b,c,d) (associativity)
983         //                  +(d,b,c,a) -> +(a,b,c,d) (canonicalization)
984         //                  +(...,x,*(x,c1),*(x,c2)) -> +(...,*(x,1+c1+c2)) (c1, c2 numeric())
985         //                  (same for (+,*) -> (*,^)
986
987         make_flat(v);
988 #if EXPAIRSEQ_USE_HASHTAB
989         combine_same_terms();
990 #else
991         canonicalize();
992         combine_same_terms_sorted_seq();
993 #endif // EXPAIRSEQ_USE_HASHTAB
994 }
995
996 /** Combine this expairseq with argument exvector.
997  *  It cares for associativity as well as for special handling of numerics. */
998 void expairseq::make_flat(const exvector &v)
999 {
1000         exvector::const_iterator cit;
1001         
1002         // count number of operands which are of same expairseq derived type
1003         // and their cumulative number of operands
1004         int nexpairseqs = 0;
1005         int noperands = 0;
1006         
1007         cit = v.begin();
1008         while (cit!=v.end()) {
1009                 if (ex_to<basic>(*cit).tinfo()==this->tinfo()) {
1010                         ++nexpairseqs;
1011                         noperands += ex_to<expairseq>(*cit).seq.size();
1012                 }
1013                 ++cit;
1014         }
1015         
1016         // reserve seq and coeffseq which will hold all operands
1017         seq.reserve(v.size()+noperands-nexpairseqs);
1018         
1019         // copy elements and split off numerical part
1020         exvector dummy_indices;
1021         cit = v.begin();
1022         while (cit!=v.end()) {
1023                 if (ex_to<basic>(*cit).tinfo()==this->tinfo()) {
1024                         const expairseq *subseqref;
1025                         ex newfactor;
1026                         if(is_a<mul>(*cit))
1027                         {
1028                                 exvector dummies_of_factor = get_all_dummy_indices(*cit);
1029                                 sort(dummies_of_factor.begin(), dummies_of_factor.end(), ex_is_less());
1030                                 newfactor = rename_dummy_indices_uniquely(dummy_indices, dummies_of_factor, *cit);
1031                                 subseqref = &(ex_to<expairseq>(newfactor));
1032                                 exvector new_dummy_indices;
1033                                 set_union(dummy_indices.begin(), dummy_indices.end(), dummies_of_factor.begin(), dummies_of_factor.end(), std::back_insert_iterator<exvector>(new_dummy_indices), ex_is_less());
1034                                 dummy_indices.swap(new_dummy_indices);
1035                         }
1036                         else
1037                                 subseqref = &ex_to<expairseq>(*cit);
1038                         combine_overall_coeff(subseqref->overall_coeff);
1039                         epvector::const_iterator cit_s = subseqref->seq.begin();
1040                         while (cit_s!=subseqref->seq.end()) {
1041                                 seq.push_back(*cit_s);
1042                                 ++cit_s;
1043                         }
1044                 } else {
1045                         if (is_exactly_a<numeric>(*cit))
1046                                 combine_overall_coeff(*cit);
1047                         else
1048                                 seq.push_back(split_ex_to_pair(*cit));
1049                 }
1050                 ++cit;
1051         }
1052 }
1053
1054 /** Combine this expairseq with argument epvector.
1055  *  It cares for associativity as well as for special handling of numerics. */
1056 void expairseq::make_flat(const epvector &v)
1057 {
1058         epvector::const_iterator cit;
1059         
1060         // count number of operands which are of same expairseq derived type
1061         // and their cumulative number of operands
1062         int nexpairseqs = 0;
1063         int noperands = 0;
1064         
1065         cit = v.begin();
1066         while (cit!=v.end()) {
1067                 if (ex_to<basic>(cit->rest).tinfo()==this->tinfo()) {
1068                         ++nexpairseqs;
1069                         noperands += ex_to<expairseq>(cit->rest).seq.size();
1070                 }
1071                 ++cit;
1072         }
1073         
1074         // reserve seq and coeffseq which will hold all operands
1075         seq.reserve(v.size()+noperands-nexpairseqs);
1076         
1077         // copy elements and split off numerical part
1078         cit = v.begin();
1079         while (cit!=v.end()) {
1080                 if (ex_to<basic>(cit->rest).tinfo()==this->tinfo() &&
1081                     this->can_make_flat(*cit)) {
1082                         const expairseq &subseqref = ex_to<expairseq>(cit->rest);
1083                         combine_overall_coeff(ex_to<numeric>(subseqref.overall_coeff),
1084                                                             ex_to<numeric>(cit->coeff));
1085                         epvector::const_iterator cit_s = subseqref.seq.begin();
1086                         while (cit_s!=subseqref.seq.end()) {
1087                                 seq.push_back(expair(cit_s->rest,
1088                                                      ex_to<numeric>(cit_s->coeff).mul_dyn(ex_to<numeric>(cit->coeff))));
1089                                 //seq.push_back(combine_pair_with_coeff_to_pair(*cit_s,
1090                                 //                                              (*cit).coeff));
1091                                 ++cit_s;
1092                         }
1093                 } else {
1094                         if (cit->is_canonical_numeric())
1095                                 combine_overall_coeff(cit->rest);
1096                         else
1097                                 seq.push_back(*cit);
1098                 }
1099                 ++cit;
1100         }
1101 }
1102
1103 /** Brings this expairseq into a sorted (canonical) form. */
1104 void expairseq::canonicalize()
1105 {
1106         std::sort(seq.begin(), seq.end(), expair_rest_is_less());
1107 }
1108
1109
1110 /** Compact a presorted expairseq by combining all matching expairs to one
1111  *  each.  On an add object, this is responsible for 2*x+3*x+y -> 5*x+y, for
1112  *  instance. */
1113 void expairseq::combine_same_terms_sorted_seq()
1114 {
1115         if (seq.size()<2)
1116                 return;
1117
1118         bool needs_further_processing = false;
1119
1120         epvector::iterator itin1 = seq.begin();
1121         epvector::iterator itin2 = itin1+1;
1122         epvector::iterator itout = itin1;
1123         epvector::iterator last = seq.end();
1124         // must_copy will be set to true the first time some combination is 
1125         // possible from then on the sequence has changed and must be compacted
1126         bool must_copy = false;
1127         while (itin2!=last) {
1128                 if (itin1->rest.compare(itin2->rest)==0) {
1129                         itin1->coeff = ex_to<numeric>(itin1->coeff).
1130                                        add_dyn(ex_to<numeric>(itin2->coeff));
1131                         if (expair_needs_further_processing(itin1))
1132                                 needs_further_processing = true;
1133                         must_copy = true;
1134                 } else {
1135                         if (!ex_to<numeric>(itin1->coeff).is_zero()) {
1136                                 if (must_copy)
1137                                         *itout = *itin1;
1138                                 ++itout;
1139                         }
1140                         itin1 = itin2;
1141                 }
1142                 ++itin2;
1143         }
1144         if (!ex_to<numeric>(itin1->coeff).is_zero()) {
1145                 if (must_copy)
1146                         *itout = *itin1;
1147                 ++itout;
1148         }
1149         if (itout!=last)
1150                 seq.erase(itout,last);
1151
1152         if (needs_further_processing) {
1153                 epvector v = seq;
1154                 seq.clear();
1155                 construct_from_epvector(v);
1156         }
1157 }
1158
1159 #if EXPAIRSEQ_USE_HASHTAB
1160
1161 unsigned expairseq::calc_hashtabsize(unsigned sz) const
1162 {
1163         unsigned size;
1164         unsigned nearest_power_of_2 = 1 << log2(sz);
1165         // if (nearest_power_of_2 < maxhashtabsize/hashtabfactor) {
1166         //  size = nearest_power_of_2*hashtabfactor;
1167         size = nearest_power_of_2/hashtabfactor;
1168         if (size<minhashtabsize)
1169                 return 0;
1170
1171         // hashtabsize must be a power of 2
1172         GINAC_ASSERT((1U << log2(size))==size);
1173         return size;
1174 }
1175
1176 unsigned expairseq::calc_hashindex(const ex &e) const
1177 {
1178         // calculate hashindex
1179         unsigned hashindex;
1180         if (is_a<numeric>(e)) {
1181                 hashindex = hashmask;
1182         } else {
1183                 hashindex = e.gethash() & hashmask;
1184                 // last hashtab entry is reserved for numerics
1185                 if (hashindex==hashmask) hashindex = 0;
1186         }
1187         GINAC_ASSERT((hashindex<hashtabsize)||(hashtabsize==0));
1188         return hashindex;
1189 }
1190
1191 void expairseq::shrink_hashtab()
1192 {
1193         unsigned new_hashtabsize;
1194         while (hashtabsize!=(new_hashtabsize=calc_hashtabsize(seq.size()))) {
1195                 GINAC_ASSERT(new_hashtabsize<hashtabsize);
1196                 if (new_hashtabsize==0) {
1197                         hashtab.clear();
1198                         hashtabsize = 0;
1199                         canonicalize();
1200                         return;
1201                 }
1202                 
1203                 // shrink by a factor of 2
1204                 unsigned half_hashtabsize = hashtabsize/2;
1205                 for (unsigned i=0; i<half_hashtabsize-1; ++i)
1206                         hashtab[i].merge(hashtab[i+half_hashtabsize],epp_is_less());
1207                 // special treatment for numeric hashes
1208                 hashtab[0].merge(hashtab[half_hashtabsize-1],epp_is_less());
1209                 hashtab[half_hashtabsize-1] = hashtab[hashtabsize-1];
1210                 hashtab.resize(half_hashtabsize);
1211                 hashtabsize = half_hashtabsize;
1212                 hashmask = hashtabsize-1;
1213         }
1214 }
1215
1216 void expairseq::remove_hashtab_entry(epvector::const_iterator element)
1217 {
1218         if (hashtabsize==0)
1219                 return; // nothing to do
1220         
1221         // calculate hashindex of element to be deleted
1222         unsigned hashindex = calc_hashindex((*element).rest);
1223
1224         // find it in hashtab and remove it
1225         epplist &eppl = hashtab[hashindex];
1226         epplist::iterator epplit = eppl.begin();
1227         bool erased = false;
1228         while (epplit!=eppl.end()) {
1229                 if (*epplit == element) {
1230                         eppl.erase(epplit);
1231                         erased = true;
1232                         break;
1233                 }
1234                 ++epplit;
1235         }
1236         if (!erased) {
1237                 std::cout << "tried to erase " << element-seq.begin() << std::endl;
1238                 std::cout << "size " << seq.end()-seq.begin() << std::endl;
1239
1240                 unsigned hashindex = calc_hashindex(element->rest);
1241                 epplist &eppl = hashtab[hashindex];
1242                 epplist::iterator epplit = eppl.begin();
1243                 bool erased = false;
1244                 while (epplit!=eppl.end()) {
1245                         if (*epplit == element) {
1246                                 eppl.erase(epplit);
1247                                 erased = true;
1248                                 break;
1249                         }
1250                         ++epplit;
1251                 }
1252                 GINAC_ASSERT(erased);
1253         }
1254         GINAC_ASSERT(erased);
1255 }
1256
1257 void expairseq::move_hashtab_entry(epvector::const_iterator oldpos,
1258                                    epvector::iterator newpos)
1259 {
1260         GINAC_ASSERT(hashtabsize!=0);
1261         
1262         // calculate hashindex of element which was moved
1263         unsigned hashindex=calc_hashindex((*newpos).rest);
1264
1265         // find it in hashtab and modify it
1266         epplist &eppl = hashtab[hashindex];
1267         epplist::iterator epplit = eppl.begin();
1268         while (epplit!=eppl.end()) {
1269                 if (*epplit == oldpos) {
1270                         *epplit = newpos;
1271                         break;
1272                 }
1273                 ++epplit;
1274         }
1275         GINAC_ASSERT(epplit!=eppl.end());
1276 }
1277
1278 void expairseq::sorted_insert(epplist &eppl, epvector::const_iterator elem)
1279 {
1280         epplist::const_iterator current = eppl.begin();
1281         while ((current!=eppl.end()) && ((*current)->is_less(*elem))) {
1282                 ++current;
1283         }
1284         eppl.insert(current,elem);
1285 }    
1286
1287 void expairseq::build_hashtab_and_combine(epvector::iterator &first_numeric,
1288                                           epvector::iterator &last_non_zero,
1289                                           std::vector<bool> &touched,
1290                                           unsigned &number_of_zeroes)
1291 {
1292         epp current = seq.begin();
1293
1294         while (current!=first_numeric) {
1295                 if (is_exactly_a<numeric>(current->rest)) {
1296                         --first_numeric;
1297                         iter_swap(current,first_numeric);
1298                 } else {
1299                         // calculate hashindex
1300                         unsigned currenthashindex = calc_hashindex(current->rest);
1301
1302                         // test if there is already a matching expair in the hashtab-list
1303                         epplist &eppl=hashtab[currenthashindex];
1304                         epplist::iterator epplit = eppl.begin();
1305                         while (epplit!=eppl.end()) {
1306                                 if (current->rest.is_equal((*epplit)->rest))
1307                                         break;
1308                                 ++epplit;
1309                         }
1310                         if (epplit==eppl.end()) {
1311                                 // no matching expair found, append this to end of list
1312                                 sorted_insert(eppl,current);
1313                                 ++current;
1314                         } else {
1315                                 // epplit points to a matching expair, combine it with current
1316                                 (*epplit)->coeff = ex_to<numeric>((*epplit)->coeff).
1317                                                    add_dyn(ex_to<numeric>(current->coeff));
1318                                 
1319                                 // move obsolete current expair to end by swapping with last_non_zero element
1320                                 // if this was a numeric, it is swapped with the expair before first_numeric 
1321                                 iter_swap(current,last_non_zero);
1322                                 --first_numeric;
1323                                 if (first_numeric!=last_non_zero) iter_swap(first_numeric,current);
1324                                 --last_non_zero;
1325                                 ++number_of_zeroes;
1326                                 // test if combined term has coeff 0 and can be removed is done later
1327                                 touched[(*epplit)-seq.begin()] = true;
1328                         }
1329                 }
1330         }
1331 }
1332
1333 void expairseq::drop_coeff_0_terms(epvector::iterator &first_numeric,
1334                                    epvector::iterator &last_non_zero,
1335                                    std::vector<bool> &touched,
1336                                    unsigned &number_of_zeroes)
1337 {
1338         // move terms with coeff 0 to end and remove them from hashtab
1339         // check only those elements which have been touched
1340         epp current = seq.begin();
1341         size_t i = 0;
1342         while (current!=first_numeric) {
1343                 if (!touched[i]) {
1344                         ++current;
1345                         ++i;
1346                 } else if (!ex_to<numeric>((*current).coeff).is_zero()) {
1347                         ++current;
1348                         ++i;
1349                 } else {
1350                         remove_hashtab_entry(current);
1351                         
1352                         // move element to the end, unless it is already at the end
1353                         if (current!=last_non_zero) {
1354                                 iter_swap(current,last_non_zero);
1355                                 --first_numeric;
1356                                 bool numeric_swapped = first_numeric!=last_non_zero;
1357                                 if (numeric_swapped)
1358                                         iter_swap(first_numeric,current);
1359                                 epvector::iterator changed_entry;
1360
1361                                 if (numeric_swapped)
1362                                         changed_entry = first_numeric;
1363                                 else
1364                                         changed_entry = last_non_zero;
1365                                 
1366                                 --last_non_zero;
1367                                 ++number_of_zeroes;
1368
1369                                 if (first_numeric!=current) {
1370
1371                                         // change entry in hashtab which referred to first_numeric or last_non_zero to current
1372                                         move_hashtab_entry(changed_entry,current);
1373                                         touched[current-seq.begin()] = touched[changed_entry-seq.begin()];
1374                                 }
1375                         } else {
1376                                 --first_numeric;
1377                                 --last_non_zero;
1378                                 ++number_of_zeroes;
1379                         }
1380                 }
1381         }
1382         GINAC_ASSERT(i==current-seq.begin());
1383 }
1384
1385 /** True if one of the coeffs vanishes, otherwise false.
1386  *  This would be an invariant violation, so this should only be used for
1387  *  debugging purposes. */
1388 bool expairseq::has_coeff_0() const
1389 {
1390         epvector::const_iterator i = seq.begin(), end = seq.end();
1391         while (i != end) {
1392                 if (i->coeff.is_zero())
1393                         return true;
1394                 ++i;
1395         }
1396         return false;
1397 }
1398
1399 void expairseq::add_numerics_to_hashtab(epvector::iterator first_numeric,
1400                                                                                 epvector::const_iterator last_non_zero)
1401 {
1402         if (first_numeric == seq.end()) return; // no numerics
1403         
1404         epvector::const_iterator current = first_numeric, last = last_non_zero + 1;
1405         while (current != last) {
1406                 sorted_insert(hashtab[hashmask], current);
1407                 ++current;
1408         }
1409 }
1410
1411 void expairseq::combine_same_terms()
1412 {
1413         // combine same terms, drop term with coeff 0, move numerics to end
1414         
1415         // calculate size of hashtab
1416         hashtabsize = calc_hashtabsize(seq.size());
1417         
1418         // hashtabsize is a power of 2
1419         hashmask = hashtabsize-1;
1420         
1421         // allocate hashtab
1422         hashtab.clear();
1423         hashtab.resize(hashtabsize);
1424         
1425         if (hashtabsize==0) {
1426                 canonicalize();
1427                 combine_same_terms_sorted_seq();
1428                 GINAC_ASSERT(!has_coeff_0());
1429                 return;
1430         }
1431         
1432         // iterate through seq, move numerics to end,
1433         // fill hashtab and combine same terms
1434         epvector::iterator first_numeric = seq.end();
1435         epvector::iterator last_non_zero = seq.end()-1;
1436         
1437         size_t num = seq.size();
1438         std::vector<bool> touched(num);
1439         
1440         unsigned number_of_zeroes = 0;
1441         
1442         GINAC_ASSERT(!has_coeff_0());
1443         build_hashtab_and_combine(first_numeric,last_non_zero,touched,number_of_zeroes);
1444         
1445         // there should not be any terms with coeff 0 from the beginning,
1446         // so it should be safe to skip this step
1447         if (number_of_zeroes!=0) {
1448                 drop_coeff_0_terms(first_numeric,last_non_zero,touched,number_of_zeroes);
1449         }
1450         
1451         add_numerics_to_hashtab(first_numeric,last_non_zero);
1452         
1453         // pop zero elements
1454         for (unsigned i=0; i<number_of_zeroes; ++i) {
1455                 seq.pop_back();
1456         }
1457         
1458         // shrink hashtabsize to calculated value
1459         GINAC_ASSERT(!has_coeff_0());
1460         
1461         shrink_hashtab();
1462         
1463         GINAC_ASSERT(!has_coeff_0());
1464 }
1465
1466 #endif // EXPAIRSEQ_USE_HASHTAB
1467
1468 /** Check if this expairseq is in sorted (canonical) form.  Useful mainly for
1469  *  debugging or in assertions since being sorted is an invariance. */
1470 bool expairseq::is_canonical() const
1471 {
1472         if (seq.size() <= 1)
1473                 return 1;
1474         
1475 #if EXPAIRSEQ_USE_HASHTAB
1476         if (hashtabsize > 0) return 1; // not canoncalized
1477 #endif // EXPAIRSEQ_USE_HASHTAB
1478         
1479         epvector::const_iterator it = seq.begin(), itend = seq.end();
1480         epvector::const_iterator it_last = it;
1481         for (++it; it!=itend; it_last=it, ++it) {
1482                 if (!(it_last->is_less(*it) || it_last->is_equal(*it))) {
1483                         if (!is_exactly_a<numeric>(it_last->rest) ||
1484                                 !is_exactly_a<numeric>(it->rest)) {
1485                                 // double test makes it easier to set a breakpoint...
1486                                 if (!is_exactly_a<numeric>(it_last->rest) ||
1487                                         !is_exactly_a<numeric>(it->rest)) {
1488                                         printpair(std::clog, *it_last, 0);
1489                                         std::clog << ">";
1490                                         printpair(std::clog, *it, 0);
1491                                         std::clog << "\n";
1492                                         std::clog << "pair1:" << std::endl;
1493                                         it_last->rest.print(print_tree(std::clog));
1494                                         it_last->coeff.print(print_tree(std::clog));
1495                                         std::clog << "pair2:" << std::endl;
1496                                         it->rest.print(print_tree(std::clog));
1497                                         it->coeff.print(print_tree(std::clog));
1498                                         return 0;
1499                                 }
1500                         }
1501                 }
1502         }
1503         return 1;
1504 }
1505
1506
1507 /** Member-wise expand the expairs in this sequence.
1508  *
1509  *  @see expairseq::expand()
1510  *  @return pointer to epvector containing expanded pairs or zero pointer,
1511  *  if no members were changed. */
1512 std::auto_ptr<epvector> expairseq::expandchildren(unsigned options) const
1513 {
1514         const epvector::const_iterator last = seq.end();
1515         epvector::const_iterator cit = seq.begin();
1516         while (cit!=last) {
1517                 const ex &expanded_ex = cit->rest.expand(options);
1518                 if (!are_ex_trivially_equal(cit->rest,expanded_ex)) {
1519                         
1520                         // something changed, copy seq, eval and return it
1521                         std::auto_ptr<epvector> s(new epvector);
1522                         s->reserve(seq.size());
1523                         
1524                         // copy parts of seq which are known not to have changed
1525                         epvector::const_iterator cit2 = seq.begin();
1526                         while (cit2!=cit) {
1527                                 s->push_back(*cit2);
1528                                 ++cit2;
1529                         }
1530
1531                         // copy first changed element
1532                         s->push_back(combine_ex_with_coeff_to_pair(expanded_ex,
1533                                                                    cit2->coeff));
1534                         ++cit2;
1535
1536                         // copy rest
1537                         while (cit2!=last) {
1538                                 s->push_back(combine_ex_with_coeff_to_pair(cit2->rest.expand(options),
1539                                                                            cit2->coeff));
1540                                 ++cit2;
1541                         }
1542                         return s;
1543                 }
1544                 ++cit;
1545         }
1546         
1547         return std::auto_ptr<epvector>(0); // signalling nothing has changed
1548 }
1549
1550
1551 /** Member-wise evaluate the expairs in this sequence.
1552  *
1553  *  @see expairseq::eval()
1554  *  @return pointer to epvector containing evaluated pairs or zero pointer,
1555  *  if no members were changed. */
1556 std::auto_ptr<epvector> expairseq::evalchildren(int level) const
1557 {
1558         // returns a NULL pointer if nothing had to be evaluated
1559         // returns a pointer to a newly created epvector otherwise
1560         // (which has to be deleted somewhere else)
1561
1562         if (level==1)
1563                 return std::auto_ptr<epvector>(0);
1564         
1565         if (level == -max_recursion_level)
1566                 throw(std::runtime_error("max recursion level reached"));
1567         
1568         --level;
1569         epvector::const_iterator last = seq.end();
1570         epvector::const_iterator cit = seq.begin();
1571         while (cit!=last) {
1572                 const ex &evaled_ex = cit->rest.eval(level);
1573                 if (!are_ex_trivially_equal(cit->rest,evaled_ex)) {
1574                         
1575                         // something changed, copy seq, eval and return it
1576                         std::auto_ptr<epvector> s(new epvector);
1577                         s->reserve(seq.size());
1578                         
1579                         // copy parts of seq which are known not to have changed
1580                         epvector::const_iterator cit2=seq.begin();
1581                         while (cit2!=cit) {
1582                                 s->push_back(*cit2);
1583                                 ++cit2;
1584                         }
1585
1586                         // copy first changed element
1587                         s->push_back(combine_ex_with_coeff_to_pair(evaled_ex,
1588                                                                    cit2->coeff));
1589                         ++cit2;
1590
1591                         // copy rest
1592                         while (cit2!=last) {
1593                                 s->push_back(combine_ex_with_coeff_to_pair(cit2->rest.eval(level),
1594                                                                            cit2->coeff));
1595                                 ++cit2;
1596                         }
1597                         return s;
1598                 }
1599                 ++cit;
1600         }
1601         
1602         return std::auto_ptr<epvector>(0); // signalling nothing has changed
1603 }
1604
1605 class safe_inserter
1606 {
1607         public:
1608                 safe_inserter(const ex&, const bool disable_renaming=false);
1609                 std::auto_ptr<epvector> getseq(){return epv;}
1610                 void insert_old_pair(const expair &p)
1611                 {
1612                         epv->push_back(p);
1613                 }
1614                 void insert_new_pair(const expair &p, const ex &orig_ex);
1615         private:
1616                 std::auto_ptr<epvector> epv;
1617                 bool dodummies;
1618                 exvector dummy_indices;
1619                 void update_dummy_indices(const exvector&);
1620 };
1621
1622 safe_inserter::safe_inserter(const ex&e, const bool disable_renaming)
1623                 :epv(new epvector)
1624 {
1625         epv->reserve(e.nops());
1626         dodummies=is_a<mul>(e);
1627         if(disable_renaming)
1628                 dodummies=false;
1629         if(dodummies) {
1630                 dummy_indices = get_all_dummy_indices_safely(e);
1631                 sort(dummy_indices.begin(), dummy_indices.end(), ex_is_less());
1632         }
1633 }
1634
1635 void safe_inserter::update_dummy_indices(const exvector &v)
1636 {
1637         exvector new_dummy_indices;
1638         set_union(dummy_indices.begin(), dummy_indices.end(), v.begin(), v.end(),
1639                 std::back_insert_iterator<exvector>(new_dummy_indices), ex_is_less());
1640         dummy_indices.swap(new_dummy_indices);
1641 }
1642
1643 void safe_inserter::insert_new_pair(const expair &p, const ex &orig_ex)
1644 {
1645         if(!dodummies) {
1646                 epv->push_back(p);
1647                 return;
1648         }
1649         exvector dummies_of_factor = get_all_dummy_indices_safely(p.rest);
1650         if(dummies_of_factor.size() == 0) {
1651                 epv->push_back(p);
1652                 return;
1653         }
1654         sort(dummies_of_factor.begin(), dummies_of_factor.end(), ex_is_less());
1655         exvector dummies_of_orig_ex = get_all_dummy_indices_safely(orig_ex);
1656         sort(dummies_of_orig_ex.begin(), dummies_of_orig_ex.end(), ex_is_less());
1657         exvector new_dummy_indices;
1658         new_dummy_indices.reserve(dummy_indices.size());
1659         set_difference(dummy_indices.begin(), dummy_indices.end(), dummies_of_orig_ex.begin(), dummies_of_orig_ex.end(),
1660                 std::back_insert_iterator<exvector>(new_dummy_indices), ex_is_less());
1661         dummy_indices.swap(new_dummy_indices);
1662         ex newfactor = rename_dummy_indices_uniquely(dummy_indices, dummies_of_factor, p.rest);
1663         update_dummy_indices(dummies_of_factor);
1664         epv -> push_back(expair(newfactor, p.coeff));
1665 }
1666
1667 /** Member-wise substitute in this sequence.
1668  *
1669  *  @see expairseq::subs()
1670  *  @return pointer to epvector containing pairs after application of subs,
1671  *    or NULL pointer if no members were changed. */
1672 std::auto_ptr<epvector> expairseq::subschildren(const exmap & m, unsigned options) const
1673 {
1674         // When any of the objects to be substituted is a product or power
1675         // we have to recombine the pairs because the numeric coefficients may
1676         // be part of the search pattern.
1677         if (!(options & (subs_options::pattern_is_product | subs_options::pattern_is_not_product))) {
1678
1679                 // Search the list of substitutions and cache our findings
1680                 for (exmap::const_iterator it = m.begin(); it != m.end(); ++it) {
1681                         if (is_exactly_a<mul>(it->first) || is_exactly_a<power>(it->first)) {
1682                                 options |= subs_options::pattern_is_product;
1683                                 break;
1684                         }
1685                 }
1686                 if (!(options & subs_options::pattern_is_product))
1687                         options |= subs_options::pattern_is_not_product;
1688         }
1689
1690         if (options & subs_options::pattern_is_product) {
1691
1692                 // Substitute in the recombined pairs
1693                 epvector::const_iterator cit = seq.begin(), last = seq.end();
1694                 while (cit != last) {
1695
1696                         const ex &orig_ex = recombine_pair_to_ex(*cit);
1697                         const ex &subsed_ex = orig_ex.subs(m, options);
1698                         if (!are_ex_trivially_equal(orig_ex, subsed_ex)) {
1699
1700                                 // Something changed, copy seq, subs and return it
1701                                 safe_inserter s(*this, options & subs_options::no_index_renaming);
1702
1703                                 // Copy parts of seq which are known not to have changed
1704                                 for(epvector::const_iterator i=seq.begin(); i!=cit; ++i)
1705                                         s.insert_old_pair(*i);
1706
1707                                 // Copy first changed element
1708                                 s.insert_new_pair(split_ex_to_pair(subsed_ex), orig_ex);
1709                                 ++cit;
1710
1711                                 // Copy rest
1712                                 while (cit != last) {
1713                                         ex orig_ex = recombine_pair_to_ex(*cit);
1714                                         ex subsed_ex = orig_ex.subs(m, options);
1715                                         if(are_ex_trivially_equal(orig_ex, subsed_ex))
1716                                                 s.insert_old_pair(*cit);
1717                                         else
1718                                                 s.insert_new_pair(split_ex_to_pair(subsed_ex), orig_ex);
1719                                         ++cit;
1720                                 }
1721                                 return s.getseq();
1722                         }
1723
1724                         ++cit;
1725                 }
1726
1727         } else {
1728
1729                 // Substitute only in the "rest" part of the pairs
1730                 epvector::const_iterator cit = seq.begin(), last = seq.end();
1731                 while (cit != last) {
1732
1733                         const ex &subsed_ex = cit->rest.subs(m, options);
1734                         if (!are_ex_trivially_equal(cit->rest, subsed_ex)) {
1735                         
1736                                 // Something changed, copy seq, subs and return it
1737                                 safe_inserter s(*this, options & subs_options::no_index_renaming);
1738
1739                                 // Copy parts of seq which are known not to have changed
1740                                 for(epvector::const_iterator i=seq.begin(); i!=cit; ++i)
1741                                         s.insert_old_pair(*i);
1742                         
1743                                 // Copy first changed element
1744                                 s.insert_new_pair(combine_ex_with_coeff_to_pair(subsed_ex, cit->coeff), cit->rest);
1745                                 ++cit;
1746
1747                                 // Copy rest
1748                                 while (cit != last) {
1749                                         const ex &orig_ex = cit->rest;
1750                                         const ex &subsed_ex = cit->rest.subs(m, options);
1751                                         if(are_ex_trivially_equal(orig_ex, subsed_ex))
1752                                                 s.insert_old_pair(*cit);
1753                                         else
1754                                                 s.insert_new_pair(combine_ex_with_coeff_to_pair(subsed_ex, cit->coeff), orig_ex);
1755                                         ++cit;
1756                                 }
1757                                 return s.getseq();
1758                         }
1759
1760                         ++cit;
1761                 }
1762         }
1763         
1764         // Nothing has changed
1765         return std::auto_ptr<epvector>(0);
1766 }
1767
1768 //////////
1769 // static member variables
1770 //////////
1771
1772 #if EXPAIRSEQ_USE_HASHTAB
1773 unsigned expairseq::maxhashtabsize = 0x4000000U;
1774 unsigned expairseq::minhashtabsize = 0x1000U;
1775 unsigned expairseq::hashtabfactor = 1;
1776 #endif // EXPAIRSEQ_USE_HASHTAB
1777
1778 } // namespace GiNaC