1 /** @file expairseq.cpp
3 * Implementation of sequences of expression pairs. */
6 * GiNaC Copyright (C) 1999-2007 Johannes Gutenberg University Mainz, Germany
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.
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.
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
28 #include "expairseq.h"
33 #include "relational.h"
36 #include "operators.h"
40 #if EXPAIRSEQ_USE_HASHTAB
42 #endif // EXPAIRSEQ_USE_HASHTAB
47 GINAC_IMPLEMENT_REGISTERED_CLASS_OPT(expairseq, basic,
48 print_func<print_context>(&expairseq::do_print).
49 print_func<print_tree>(&expairseq::do_print_tree))
59 bool operator()(const epp &lh, const epp &rh) const
61 return (*lh).is_less(*rh);
66 // default constructor
71 expairseq::expairseq() : inherited(&expairseq::tinfo_static)
72 #if EXPAIRSEQ_USE_HASHTAB
74 #endif // EXPAIRSEQ_USE_HASHTAB
80 /** For use by copy ctor and assignment operator. */
81 void expairseq::copy(const expairseq &other)
84 overall_coeff = other.overall_coeff;
85 #if EXPAIRSEQ_USE_HASHTAB
87 hashtabsize = other.hashtabsize;
89 hashmask = other.hashmask;
90 hashtab.resize(hashtabsize);
91 epvector::const_iterator osb = other.seq.begin();
92 for (unsigned i=0; i<hashtabsize; ++i) {
94 for (epplist::const_iterator cit=other.hashtab[i].begin();
95 cit!=other.hashtab[i].end(); ++cit) {
96 hashtab[i].push_back(seq.begin()+((*cit)-osb));
102 #endif // EXPAIRSEQ_USE_HASHTAB
107 // other constructors
110 expairseq::expairseq(const ex &lh, const ex &rh) : inherited(&expairseq::tinfo_static)
112 construct_from_2_ex(lh,rh);
113 GINAC_ASSERT(is_canonical());
116 expairseq::expairseq(const exvector &v) : inherited(&expairseq::tinfo_static)
118 construct_from_exvector(v);
119 GINAC_ASSERT(is_canonical());
122 expairseq::expairseq(const epvector &v, const ex &oc, bool do_index_renaming)
123 : inherited(&expairseq::tinfo_static), overall_coeff(oc)
125 GINAC_ASSERT(is_a<numeric>(oc));
126 construct_from_epvector(v, do_index_renaming);
127 GINAC_ASSERT(is_canonical());
130 expairseq::expairseq(std::auto_ptr<epvector> vp, const ex &oc, bool do_index_renaming)
131 : inherited(&expairseq::tinfo_static), overall_coeff(oc)
133 GINAC_ASSERT(vp.get()!=0);
134 GINAC_ASSERT(is_a<numeric>(oc));
135 construct_from_epvector(*vp, do_index_renaming);
136 GINAC_ASSERT(is_canonical());
143 expairseq::expairseq(const archive_node &n, lst &sym_lst) : inherited(n, sym_lst)
144 #if EXPAIRSEQ_USE_HASHTAB
148 archive_node::archive_node_cit first = n.find_first("rest");
149 archive_node::archive_node_cit last = n.find_last("coeff");
151 seq.reserve((last-first)/2);
153 for (archive_node::archive_node_cit loc = first; loc < last;) {
156 n.find_ex_by_loc(loc++, rest, sym_lst);
157 n.find_ex_by_loc(loc++, coeff, sym_lst);
158 seq.push_back(expair(rest, coeff));
161 n.find_ex("overall_coeff", overall_coeff, sym_lst);
164 GINAC_ASSERT(is_canonical());
167 void expairseq::archive(archive_node &n) const
169 inherited::archive(n);
170 epvector::const_iterator i = seq.begin(), iend = seq.end();
172 n.add_ex("rest", i->rest);
173 n.add_ex("coeff", i->coeff);
176 n.add_ex("overall_coeff", overall_coeff);
179 DEFAULT_UNARCHIVE(expairseq)
182 // functions overriding virtual functions from base classes
187 void expairseq::do_print(const print_context & c, unsigned level) const
190 printseq(c, ',', precedence(), level);
194 void expairseq::do_print_tree(const print_tree & c, unsigned level) const
196 c.s << std::string(level, ' ') << class_name() << " @" << this
197 << std::hex << ", hash=0x" << hashvalue << ", flags=0x" << flags << std::dec
198 << ", nops=" << nops()
200 size_t num = seq.size();
201 for (size_t i=0; i<num; ++i) {
202 seq[i].rest.print(c, level + c.delta_indent);
203 seq[i].coeff.print(c, level + c.delta_indent);
205 c.s << std::string(level + c.delta_indent, ' ') << "-----" << std::endl;
207 if (!overall_coeff.is_equal(default_overall_coeff())) {
208 c.s << std::string(level + c.delta_indent, ' ') << "-----" << std::endl
209 << std::string(level + c.delta_indent, ' ') << "overall_coeff" << std::endl;
210 overall_coeff.print(c, level + c.delta_indent);
212 c.s << std::string(level + c.delta_indent,' ') << "=====" << std::endl;
213 #if EXPAIRSEQ_USE_HASHTAB
214 c.s << std::string(level + c.delta_indent,' ')
215 << "hashtab size " << hashtabsize << std::endl;
216 if (hashtabsize == 0) return;
218 unsigned count[MAXCOUNT+1];
219 for (int i=0; i<MAXCOUNT+1; ++i)
221 unsigned this_bin_fill;
222 unsigned cum_fill_sq = 0;
223 unsigned cum_fill = 0;
224 for (unsigned i=0; i<hashtabsize; ++i) {
226 if (hashtab[i].size() > 0) {
227 c.s << std::string(level + c.delta_indent, ' ')
228 << "bin " << i << " with entries ";
229 for (epplist::const_iterator it=hashtab[i].begin();
230 it!=hashtab[i].end(); ++it) {
231 c.s << *it-seq.begin() << " ";
235 cum_fill += this_bin_fill;
236 cum_fill_sq += this_bin_fill*this_bin_fill;
238 if (this_bin_fill<MAXCOUNT)
239 ++count[this_bin_fill];
245 double lambda = (1.0*seq.size()) / hashtabsize;
246 for (int k=0; k<MAXCOUNT; ++k) {
249 double prob = std::pow(lambda,k)/fact * std::exp(-lambda);
251 c.s << std::string(level + c.delta_indent, ' ') << "bins with " << k << " entries: "
252 << int(1000.0*count[k]/hashtabsize)/10.0 << "% (expected: "
253 << int(prob*1000)/10.0 << ")" << std::endl;
255 c.s << std::string(level + c.delta_indent, ' ') << "bins with more entries: "
256 << int(1000.0*count[MAXCOUNT]/hashtabsize)/10.0 << "% (expected: "
257 << int((1-cum_prob)*1000)/10.0 << ")" << std::endl;
259 c.s << std::string(level + c.delta_indent, ' ') << "variance: "
260 << 1.0/hashtabsize*cum_fill_sq-(1.0/hashtabsize*cum_fill)*(1.0/hashtabsize*cum_fill)
262 c.s << std::string(level + c.delta_indent, ' ') << "average fill: "
263 << (1.0*cum_fill)/hashtabsize
264 << " (should be equal to " << (1.0*seq.size())/hashtabsize << ")" << std::endl;
265 #endif // EXPAIRSEQ_USE_HASHTAB
268 bool expairseq::info(unsigned inf) const
270 if (inf == info_flags::expanded)
271 return (flags & status_flags::expanded);
272 return inherited::info(inf);
275 size_t expairseq::nops() const
277 if (overall_coeff.is_equal(default_overall_coeff()))
283 ex expairseq::op(size_t i) const
286 return recombine_pair_to_ex(seq[i]);
287 GINAC_ASSERT(!overall_coeff.is_equal(default_overall_coeff()));
288 return overall_coeff;
291 ex expairseq::map(map_function &f) const
293 std::auto_ptr<epvector> v(new epvector);
294 v->reserve(seq.size()+1);
296 epvector::const_iterator cit = seq.begin(), last = seq.end();
297 while (cit != last) {
298 v->push_back(split_ex_to_pair(f(recombine_pair_to_ex(*cit))));
302 if (overall_coeff.is_equal(default_overall_coeff()))
303 return thisexpairseq(v, default_overall_coeff(), true);
305 ex newcoeff = f(overall_coeff);
306 if(is_a<numeric>(newcoeff))
307 return thisexpairseq(v, newcoeff, true);
309 v->push_back(split_ex_to_pair(newcoeff));
310 return thisexpairseq(v, default_overall_coeff(), true);
315 /** Perform coefficient-wise automatic term rewriting rules in this class. */
316 ex expairseq::eval(int level) const
318 if ((level==1) && (flags &status_flags::evaluated))
321 std::auto_ptr<epvector> vp = evalchildren(level);
325 return (new expairseq(vp, overall_coeff))->setflag(status_flags::dynallocated | status_flags::evaluated);
328 epvector* conjugateepvector(const epvector&epv)
330 epvector *newepv = 0;
331 for (epvector::const_iterator i=epv.begin(); i!=epv.end(); ++i) {
333 newepv->push_back(i->conjugate());
336 expair x = i->conjugate();
337 if (x.is_equal(*i)) {
340 newepv = new epvector;
341 newepv->reserve(epv.size());
342 for (epvector::const_iterator j=epv.begin(); j!=i; ++j) {
343 newepv->push_back(*j);
345 newepv->push_back(x);
350 ex expairseq::conjugate() const
352 epvector* newepv = conjugateepvector(seq);
353 ex x = overall_coeff.conjugate();
354 if (!newepv && are_ex_trivially_equal(x, overall_coeff)) {
357 ex result = thisexpairseq(newepv ? *newepv : seq, x);
364 bool expairseq::is_polynomial(const ex & var) const
366 if (!is_exactly_a<add>(*this) && !is_exactly_a<mul>(*this))
367 return basic::is_polynomial(var);
368 for (epvector::const_iterator i=seq.begin(); i!=seq.end(); ++i) {
369 if (!(i->rest).is_polynomial(var))
375 bool expairseq::match(const ex & pattern, lst & repl_lst) const
377 // This differs from basic::match() because we want "a+b+c+d" to
378 // match "d+*+b" with "*" being "a+c", and we want to honor commutativity
380 if (this->tinfo() == ex_to<basic>(pattern).tinfo()) {
382 // Check whether global wildcard (one that matches the "rest of the
383 // expression", like "*" above) is present
384 bool has_global_wildcard = false;
386 for (size_t i=0; i<pattern.nops(); i++) {
387 if (is_exactly_a<wildcard>(pattern.op(i))) {
388 has_global_wildcard = true;
389 global_wildcard = pattern.op(i);
394 // Unfortunately, this is an O(N^2) operation because we can't
395 // sort the pattern in a useful way...
400 for (size_t i=0; i<nops(); i++)
401 ops.push_back(op(i));
403 // Now, for every term of the pattern, look for a matching term in
404 // the expression and remove the match
405 for (size_t i=0; i<pattern.nops(); i++) {
406 ex p = pattern.op(i);
407 if (has_global_wildcard && p.is_equal(global_wildcard))
409 exvector::iterator it = ops.begin(), itend = ops.end();
410 while (it != itend) {
411 lst::const_iterator last_el = repl_lst.end();
413 if (it->match(p, repl_lst)) {
418 lst::const_iterator next_el = last_el;
420 if(next_el == repl_lst.end())
423 repl_lst.remove_last();
427 return false; // no match found
431 if (has_global_wildcard) {
433 // Assign all the remaining terms to the global wildcard (unless
434 // it has already been matched before, in which case the matches
436 size_t num = ops.size();
437 std::auto_ptr<epvector> vp(new epvector);
439 for (size_t i=0; i<num; i++)
440 vp->push_back(split_ex_to_pair(ops[i]));
441 ex rest = thisexpairseq(vp, default_overall_coeff());
442 for (lst::const_iterator it = repl_lst.begin(); it != repl_lst.end(); ++it) {
443 if (it->op(0).is_equal(global_wildcard))
444 return rest.is_equal(it->op(1));
446 repl_lst.append(global_wildcard == rest);
451 // No global wildcard, then the match fails if there are any
452 // unmatched terms left
456 return inherited::match(pattern, repl_lst);
459 ex expairseq::subs(const exmap & m, unsigned options) const
461 std::auto_ptr<epvector> vp = subschildren(m, options);
463 return ex_to<basic>(thisexpairseq(vp, overall_coeff, true));
464 else if ((options & subs_options::algebraic) && is_exactly_a<mul>(*this))
465 return static_cast<const mul *>(this)->algebraic_subs_mul(m, options);
467 return subs_one_level(m, options);
472 int expairseq::compare_same_type(const basic &other) const
474 GINAC_ASSERT(is_a<expairseq>(other));
475 const expairseq &o = static_cast<const expairseq &>(other);
479 // compare number of elements
480 if (seq.size() != o.seq.size())
481 return (seq.size()<o.seq.size()) ? -1 : 1;
483 // compare overall_coeff
484 cmpval = overall_coeff.compare(o.overall_coeff);
488 #if EXPAIRSEQ_USE_HASHTAB
489 GINAC_ASSERT(hashtabsize==o.hashtabsize);
490 if (hashtabsize==0) {
491 #endif // EXPAIRSEQ_USE_HASHTAB
492 epvector::const_iterator cit1 = seq.begin();
493 epvector::const_iterator cit2 = o.seq.begin();
494 epvector::const_iterator last1 = seq.end();
495 epvector::const_iterator last2 = o.seq.end();
497 for (; (cit1!=last1)&&(cit2!=last2); ++cit1, ++cit2) {
498 cmpval = (*cit1).compare(*cit2);
499 if (cmpval!=0) return cmpval;
502 GINAC_ASSERT(cit1==last1);
503 GINAC_ASSERT(cit2==last2);
506 #if EXPAIRSEQ_USE_HASHTAB
509 // compare number of elements in each hashtab entry
510 for (unsigned i=0; i<hashtabsize; ++i) {
511 unsigned cursize=hashtab[i].size();
512 if (cursize != o.hashtab[i].size())
513 return (cursize < o.hashtab[i].size()) ? -1 : 1;
516 // compare individual (sorted) hashtab entries
517 for (unsigned i=0; i<hashtabsize; ++i) {
518 unsigned sz = hashtab[i].size();
520 const epplist &eppl1 = hashtab[i];
521 const epplist &eppl2 = o.hashtab[i];
522 epplist::const_iterator it1 = eppl1.begin();
523 epplist::const_iterator it2 = eppl2.begin();
524 while (it1!=eppl1.end()) {
525 cmpval = (*(*it1)).compare(*(*it2));
535 #endif // EXPAIRSEQ_USE_HASHTAB
538 bool expairseq::is_equal_same_type(const basic &other) const
540 const expairseq &o = static_cast<const expairseq &>(other);
542 // compare number of elements
543 if (seq.size()!=o.seq.size())
546 // compare overall_coeff
547 if (!overall_coeff.is_equal(o.overall_coeff))
550 #if EXPAIRSEQ_USE_HASHTAB
551 // compare number of elements in each hashtab entry
552 if (hashtabsize!=o.hashtabsize) {
553 std::cout << "this:" << std::endl;
554 print(print_tree(std::cout));
555 std::cout << "other:" << std::endl;
556 other.print(print_tree(std::cout));
559 GINAC_ASSERT(hashtabsize==o.hashtabsize);
561 if (hashtabsize==0) {
562 #endif // EXPAIRSEQ_USE_HASHTAB
563 epvector::const_iterator cit1 = seq.begin();
564 epvector::const_iterator cit2 = o.seq.begin();
565 epvector::const_iterator last1 = seq.end();
567 while (cit1!=last1) {
568 if (!(*cit1).is_equal(*cit2)) return false;
574 #if EXPAIRSEQ_USE_HASHTAB
577 for (unsigned i=0; i<hashtabsize; ++i) {
578 if (hashtab[i].size() != o.hashtab[i].size())
582 // compare individual sorted hashtab entries
583 for (unsigned i=0; i<hashtabsize; ++i) {
584 unsigned sz = hashtab[i].size();
586 const epplist &eppl1 = hashtab[i];
587 const epplist &eppl2 = o.hashtab[i];
588 epplist::const_iterator it1 = eppl1.begin();
589 epplist::const_iterator it2 = eppl2.begin();
590 while (it1!=eppl1.end()) {
591 if (!(*(*it1)).is_equal(*(*it2))) return false;
599 #endif // EXPAIRSEQ_USE_HASHTAB
602 unsigned expairseq::return_type() const
604 return return_types::noncommutative_composite;
607 unsigned expairseq::calchash() const
609 unsigned v = golden_ratio_hash((p_int)this->tinfo());
610 epvector::const_iterator i = seq.begin();
611 const epvector::const_iterator end = seq.end();
613 v ^= i->rest.gethash();
614 #if !EXPAIRSEQ_USE_HASHTAB
615 // rotation spoils commutativity!
617 v ^= i->coeff.gethash();
618 #endif // !EXPAIRSEQ_USE_HASHTAB
622 v ^= overall_coeff.gethash();
624 // store calculated hash value only if object is already evaluated
625 if (flags &status_flags::evaluated) {
626 setflag(status_flags::hash_calculated);
633 ex expairseq::expand(unsigned options) const
635 std::auto_ptr<epvector> vp = expandchildren(options);
637 return thisexpairseq(vp, overall_coeff);
639 // The terms have not changed, so it is safe to declare this expanded
640 return (options == 0) ? setflag(status_flags::expanded) : *this;
645 // new virtual functions which can be overridden by derived classes
650 /** Create an object of this type.
651 * This method works similar to a constructor. It is useful because expairseq
652 * has (at least) two possible different semantics but we want to inherit
653 * methods thus avoiding code duplication. Sometimes a method in expairseq
654 * has to create a new one of the same semantics, which cannot be done by a
655 * ctor because the name (add, mul,...) is unknown on the expaiseq level. In
656 * order for this trick to work a derived class must of course override this
658 ex expairseq::thisexpairseq(const epvector &v, const ex &oc, bool do_index_renaming) const
660 return expairseq(v, oc, do_index_renaming);
663 ex expairseq::thisexpairseq(std::auto_ptr<epvector> vp, const ex &oc, bool do_index_renaming) const
665 return expairseq(vp, oc, do_index_renaming);
668 void expairseq::printpair(const print_context & c, const expair & p, unsigned upper_precedence) const
671 p.rest.print(c, precedence());
673 p.coeff.print(c, precedence());
677 void expairseq::printseq(const print_context & c, char delim,
678 unsigned this_precedence,
679 unsigned upper_precedence) const
681 if (this_precedence <= upper_precedence)
683 epvector::const_iterator it, it_last = seq.end() - 1;
684 for (it=seq.begin(); it!=it_last; ++it) {
685 printpair(c, *it, this_precedence);
688 printpair(c, *it, this_precedence);
689 if (!overall_coeff.is_equal(default_overall_coeff())) {
691 overall_coeff.print(c, this_precedence);
694 if (this_precedence <= upper_precedence)
699 /** Form an expair from an ex, using the corresponding semantics.
700 * @see expairseq::recombine_pair_to_ex() */
701 expair expairseq::split_ex_to_pair(const ex &e) const
703 return expair(e,_ex1);
707 expair expairseq::combine_ex_with_coeff_to_pair(const ex &e,
710 GINAC_ASSERT(is_exactly_a<numeric>(c));
716 expair expairseq::combine_pair_with_coeff_to_pair(const expair &p,
719 GINAC_ASSERT(is_exactly_a<numeric>(p.coeff));
720 GINAC_ASSERT(is_exactly_a<numeric>(c));
722 return expair(p.rest,ex_to<numeric>(p.coeff).mul_dyn(ex_to<numeric>(c)));
726 /** Form an ex out of an expair, using the corresponding semantics.
727 * @see expairseq::split_ex_to_pair() */
728 ex expairseq::recombine_pair_to_ex(const expair &p) const
730 return lst(p.rest,p.coeff);
733 bool expairseq::expair_needs_further_processing(epp it)
735 #if EXPAIRSEQ_USE_HASHTAB
736 //# error "FIXME: expair_needs_further_processing not yet implemented for hashtabs, sorry. A.F."
737 #endif // EXPAIRSEQ_USE_HASHTAB
741 ex expairseq::default_overall_coeff() const
746 void expairseq::combine_overall_coeff(const ex &c)
748 GINAC_ASSERT(is_exactly_a<numeric>(overall_coeff));
749 GINAC_ASSERT(is_exactly_a<numeric>(c));
750 overall_coeff = ex_to<numeric>(overall_coeff).add_dyn(ex_to<numeric>(c));
753 void expairseq::combine_overall_coeff(const ex &c1, const ex &c2)
755 GINAC_ASSERT(is_exactly_a<numeric>(overall_coeff));
756 GINAC_ASSERT(is_exactly_a<numeric>(c1));
757 GINAC_ASSERT(is_exactly_a<numeric>(c2));
758 overall_coeff = ex_to<numeric>(overall_coeff).
759 add_dyn(ex_to<numeric>(c1).mul(ex_to<numeric>(c2)));
762 bool expairseq::can_make_flat(const expair &p) const
769 // non-virtual functions in this class
772 void expairseq::construct_from_2_ex_via_exvector(const ex &lh, const ex &rh)
778 construct_from_exvector(v);
779 #if EXPAIRSEQ_USE_HASHTAB
780 GINAC_ASSERT((hashtabsize==0)||(hashtabsize>=minhashtabsize));
781 GINAC_ASSERT(hashtabsize==calc_hashtabsize(seq.size()));
782 #endif // EXPAIRSEQ_USE_HASHTAB
785 void expairseq::construct_from_2_ex(const ex &lh, const ex &rh)
787 if (ex_to<basic>(lh).tinfo()==this->tinfo()) {
788 if (ex_to<basic>(rh).tinfo()==this->tinfo()) {
789 #if EXPAIRSEQ_USE_HASHTAB
790 unsigned totalsize = ex_to<expairseq>(lh).seq.size() +
791 ex_to<expairseq>(rh).seq.size();
792 if (calc_hashtabsize(totalsize)!=0) {
793 construct_from_2_ex_via_exvector(lh,rh);
795 #endif // EXPAIRSEQ_USE_HASHTAB
798 ex newrh=rename_dummy_indices_uniquely(lh, rh);
799 construct_from_2_expairseq(ex_to<expairseq>(lh),
800 ex_to<expairseq>(newrh));
803 construct_from_2_expairseq(ex_to<expairseq>(lh),
804 ex_to<expairseq>(rh));
805 #if EXPAIRSEQ_USE_HASHTAB
807 #endif // EXPAIRSEQ_USE_HASHTAB
810 #if EXPAIRSEQ_USE_HASHTAB
811 unsigned totalsize = ex_to<expairseq>(lh).seq.size()+1;
812 if (calc_hashtabsize(totalsize)!=0) {
813 construct_from_2_ex_via_exvector(lh, rh);
815 #endif // EXPAIRSEQ_USE_HASHTAB
816 construct_from_expairseq_ex(ex_to<expairseq>(lh), rh);
817 #if EXPAIRSEQ_USE_HASHTAB
819 #endif // EXPAIRSEQ_USE_HASHTAB
822 } else if (ex_to<basic>(rh).tinfo()==this->tinfo()) {
823 #if EXPAIRSEQ_USE_HASHTAB
824 unsigned totalsize=ex_to<expairseq>(rh).seq.size()+1;
825 if (calc_hashtabsize(totalsize)!=0) {
826 construct_from_2_ex_via_exvector(lh,rh);
828 #endif // EXPAIRSEQ_USE_HASHTAB
829 construct_from_expairseq_ex(ex_to<expairseq>(rh),lh);
830 #if EXPAIRSEQ_USE_HASHTAB
832 #endif // EXPAIRSEQ_USE_HASHTAB
836 #if EXPAIRSEQ_USE_HASHTAB
837 if (calc_hashtabsize(2)!=0) {
838 construct_from_2_ex_via_exvector(lh,rh);
842 #endif // EXPAIRSEQ_USE_HASHTAB
844 if (is_exactly_a<numeric>(lh)) {
845 if (is_exactly_a<numeric>(rh)) {
846 combine_overall_coeff(lh);
847 combine_overall_coeff(rh);
849 combine_overall_coeff(lh);
850 seq.push_back(split_ex_to_pair(rh));
853 if (is_exactly_a<numeric>(rh)) {
854 combine_overall_coeff(rh);
855 seq.push_back(split_ex_to_pair(lh));
857 expair p1 = split_ex_to_pair(lh);
858 expair p2 = split_ex_to_pair(rh);
860 int cmpval = p1.rest.compare(p2.rest);
862 p1.coeff = ex_to<numeric>(p1.coeff).add_dyn(ex_to<numeric>(p2.coeff));
863 if (!ex_to<numeric>(p1.coeff).is_zero()) {
864 // no further processing is necessary, since this
865 // one element will usually be recombined in eval()
882 void expairseq::construct_from_2_expairseq(const expairseq &s1,
885 combine_overall_coeff(s1.overall_coeff);
886 combine_overall_coeff(s2.overall_coeff);
888 epvector::const_iterator first1 = s1.seq.begin();
889 epvector::const_iterator last1 = s1.seq.end();
890 epvector::const_iterator first2 = s2.seq.begin();
891 epvector::const_iterator last2 = s2.seq.end();
893 seq.reserve(s1.seq.size()+s2.seq.size());
895 bool needs_further_processing=false;
897 while (first1!=last1 && first2!=last2) {
898 int cmpval = (*first1).rest.compare((*first2).rest);
902 const numeric &newcoeff = ex_to<numeric>(first1->coeff).
903 add(ex_to<numeric>(first2->coeff));
904 if (!newcoeff.is_zero()) {
905 seq.push_back(expair(first1->rest,newcoeff));
906 if (expair_needs_further_processing(seq.end()-1)) {
907 needs_further_processing = true;
912 } else if (cmpval<0) {
913 seq.push_back(*first1);
916 seq.push_back(*first2);
921 while (first1!=last1) {
922 seq.push_back(*first1);
925 while (first2!=last2) {
926 seq.push_back(*first2);
930 if (needs_further_processing) {
933 construct_from_epvector(v);
937 void expairseq::construct_from_expairseq_ex(const expairseq &s,
940 combine_overall_coeff(s.overall_coeff);
941 if (is_exactly_a<numeric>(e)) {
942 combine_overall_coeff(e);
947 epvector::const_iterator first = s.seq.begin();
948 epvector::const_iterator last = s.seq.end();
949 expair p = split_ex_to_pair(e);
951 seq.reserve(s.seq.size()+1);
952 bool p_pushed = false;
954 bool needs_further_processing=false;
956 // merge p into s.seq
957 while (first!=last) {
958 int cmpval = (*first).rest.compare(p.rest);
961 const numeric &newcoeff = ex_to<numeric>(first->coeff).
962 add(ex_to<numeric>(p.coeff));
963 if (!newcoeff.is_zero()) {
964 seq.push_back(expair(first->rest,newcoeff));
965 if (expair_needs_further_processing(seq.end()-1))
966 needs_further_processing = true;
971 } else if (cmpval<0) {
972 seq.push_back(*first);
982 // while loop exited because p was pushed, now push rest of s.seq
983 while (first!=last) {
984 seq.push_back(*first);
988 // while loop exited because s.seq was pushed, now push p
992 if (needs_further_processing) {
995 construct_from_epvector(v);
999 void expairseq::construct_from_exvector(const exvector &v)
1001 // simplifications: +(a,+(b,c),d) -> +(a,b,c,d) (associativity)
1002 // +(d,b,c,a) -> +(a,b,c,d) (canonicalization)
1003 // +(...,x,*(x,c1),*(x,c2)) -> +(...,*(x,1+c1+c2)) (c1, c2 numeric())
1004 // (same for (+,*) -> (*,^)
1007 #if EXPAIRSEQ_USE_HASHTAB
1008 combine_same_terms();
1011 combine_same_terms_sorted_seq();
1012 #endif // EXPAIRSEQ_USE_HASHTAB
1015 void expairseq::construct_from_epvector(const epvector &v, bool do_index_renaming)
1017 // simplifications: +(a,+(b,c),d) -> +(a,b,c,d) (associativity)
1018 // +(d,b,c,a) -> +(a,b,c,d) (canonicalization)
1019 // +(...,x,*(x,c1),*(x,c2)) -> +(...,*(x,1+c1+c2)) (c1, c2 numeric())
1020 // (same for (+,*) -> (*,^)
1022 make_flat(v, do_index_renaming);
1023 #if EXPAIRSEQ_USE_HASHTAB
1024 combine_same_terms();
1027 combine_same_terms_sorted_seq();
1028 #endif // EXPAIRSEQ_USE_HASHTAB
1031 /** Combine this expairseq with argument exvector.
1032 * It cares for associativity as well as for special handling of numerics. */
1033 void expairseq::make_flat(const exvector &v)
1035 exvector::const_iterator cit;
1037 // count number of operands which are of same expairseq derived type
1038 // and their cumulative number of operands
1039 int nexpairseqs = 0;
1043 while (cit!=v.end()) {
1044 if (ex_to<basic>(*cit).tinfo()==this->tinfo()) {
1046 noperands += ex_to<expairseq>(*cit).seq.size();
1051 // reserve seq and coeffseq which will hold all operands
1052 seq.reserve(v.size()+noperands-nexpairseqs);
1054 // copy elements and split off numerical part
1055 make_flat_inserter mf(v, this->tinfo() == &mul::tinfo_static);
1057 while (cit!=v.end()) {
1058 if (ex_to<basic>(*cit).tinfo()==this->tinfo()) {
1059 ex newfactor = mf.handle_factor(*cit, _ex1);
1060 const expairseq &subseqref = ex_to<expairseq>(newfactor);
1061 combine_overall_coeff(subseqref.overall_coeff);
1062 epvector::const_iterator cit_s = subseqref.seq.begin();
1063 while (cit_s!=subseqref.seq.end()) {
1064 seq.push_back(*cit_s);
1068 if (is_exactly_a<numeric>(*cit))
1069 combine_overall_coeff(*cit);
1071 ex newfactor = mf.handle_factor(*cit, _ex1);
1072 seq.push_back(split_ex_to_pair(newfactor));
1079 /** Combine this expairseq with argument epvector.
1080 * It cares for associativity as well as for special handling of numerics. */
1081 void expairseq::make_flat(const epvector &v, bool do_index_renaming)
1083 epvector::const_iterator cit;
1085 // count number of operands which are of same expairseq derived type
1086 // and their cumulative number of operands
1087 int nexpairseqs = 0;
1091 while (cit!=v.end()) {
1092 if (ex_to<basic>(cit->rest).tinfo()==this->tinfo()) {
1094 noperands += ex_to<expairseq>(cit->rest).seq.size();
1099 // reserve seq and coeffseq which will hold all operands
1100 seq.reserve(v.size()+noperands-nexpairseqs);
1101 make_flat_inserter mf(v, do_index_renaming);
1103 // copy elements and split off numerical part
1105 while (cit!=v.end()) {
1106 if (ex_to<basic>(cit->rest).tinfo()==this->tinfo() &&
1107 this->can_make_flat(*cit)) {
1108 ex newrest = mf.handle_factor(cit->rest, cit->coeff);
1109 const expairseq &subseqref = ex_to<expairseq>(newrest);
1110 combine_overall_coeff(ex_to<numeric>(subseqref.overall_coeff),
1111 ex_to<numeric>(cit->coeff));
1112 epvector::const_iterator cit_s = subseqref.seq.begin();
1113 while (cit_s!=subseqref.seq.end()) {
1114 seq.push_back(expair(cit_s->rest,
1115 ex_to<numeric>(cit_s->coeff).mul_dyn(ex_to<numeric>(cit->coeff))));
1116 //seq.push_back(combine_pair_with_coeff_to_pair(*cit_s,
1121 if (cit->is_canonical_numeric())
1122 combine_overall_coeff(mf.handle_factor(cit->rest, _ex1));
1124 ex rest = cit->rest;
1125 ex newrest = mf.handle_factor(rest, cit->coeff);
1126 if (are_ex_trivially_equal(newrest, rest))
1127 seq.push_back(*cit);
1129 seq.push_back(expair(newrest, cit->coeff));
1136 /** Brings this expairseq into a sorted (canonical) form. */
1137 void expairseq::canonicalize()
1139 std::sort(seq.begin(), seq.end(), expair_rest_is_less());
1143 /** Compact a presorted expairseq by combining all matching expairs to one
1144 * each. On an add object, this is responsible for 2*x+3*x+y -> 5*x+y, for
1146 void expairseq::combine_same_terms_sorted_seq()
1151 bool needs_further_processing = false;
1153 epvector::iterator itin1 = seq.begin();
1154 epvector::iterator itin2 = itin1+1;
1155 epvector::iterator itout = itin1;
1156 epvector::iterator last = seq.end();
1157 // must_copy will be set to true the first time some combination is
1158 // possible from then on the sequence has changed and must be compacted
1159 bool must_copy = false;
1160 while (itin2!=last) {
1161 if (itin1->rest.compare(itin2->rest)==0) {
1162 itin1->coeff = ex_to<numeric>(itin1->coeff).
1163 add_dyn(ex_to<numeric>(itin2->coeff));
1164 if (expair_needs_further_processing(itin1))
1165 needs_further_processing = true;
1168 if (!ex_to<numeric>(itin1->coeff).is_zero()) {
1177 if (!ex_to<numeric>(itin1->coeff).is_zero()) {
1183 seq.erase(itout,last);
1185 if (needs_further_processing) {
1188 construct_from_epvector(v);
1192 #if EXPAIRSEQ_USE_HASHTAB
1194 unsigned expairseq::calc_hashtabsize(unsigned sz) const
1197 unsigned nearest_power_of_2 = 1 << log2(sz);
1198 // if (nearest_power_of_2 < maxhashtabsize/hashtabfactor) {
1199 // size = nearest_power_of_2*hashtabfactor;
1200 size = nearest_power_of_2/hashtabfactor;
1201 if (size<minhashtabsize)
1204 // hashtabsize must be a power of 2
1205 GINAC_ASSERT((1U << log2(size))==size);
1209 unsigned expairseq::calc_hashindex(const ex &e) const
1211 // calculate hashindex
1213 if (is_a<numeric>(e)) {
1214 hashindex = hashmask;
1216 hashindex = e.gethash() & hashmask;
1217 // last hashtab entry is reserved for numerics
1218 if (hashindex==hashmask) hashindex = 0;
1220 GINAC_ASSERT((hashindex<hashtabsize)||(hashtabsize==0));
1224 void expairseq::shrink_hashtab()
1226 unsigned new_hashtabsize;
1227 while (hashtabsize!=(new_hashtabsize=calc_hashtabsize(seq.size()))) {
1228 GINAC_ASSERT(new_hashtabsize<hashtabsize);
1229 if (new_hashtabsize==0) {
1236 // shrink by a factor of 2
1237 unsigned half_hashtabsize = hashtabsize/2;
1238 for (unsigned i=0; i<half_hashtabsize-1; ++i)
1239 hashtab[i].merge(hashtab[i+half_hashtabsize],epp_is_less());
1240 // special treatment for numeric hashes
1241 hashtab[0].merge(hashtab[half_hashtabsize-1],epp_is_less());
1242 hashtab[half_hashtabsize-1] = hashtab[hashtabsize-1];
1243 hashtab.resize(half_hashtabsize);
1244 hashtabsize = half_hashtabsize;
1245 hashmask = hashtabsize-1;
1249 void expairseq::remove_hashtab_entry(epvector::const_iterator element)
1252 return; // nothing to do
1254 // calculate hashindex of element to be deleted
1255 unsigned hashindex = calc_hashindex((*element).rest);
1257 // find it in hashtab and remove it
1258 epplist &eppl = hashtab[hashindex];
1259 epplist::iterator epplit = eppl.begin();
1260 bool erased = false;
1261 while (epplit!=eppl.end()) {
1262 if (*epplit == element) {
1270 std::cout << "tried to erase " << element-seq.begin() << std::endl;
1271 std::cout << "size " << seq.end()-seq.begin() << std::endl;
1273 unsigned hashindex = calc_hashindex(element->rest);
1274 epplist &eppl = hashtab[hashindex];
1275 epplist::iterator epplit = eppl.begin();
1276 bool erased = false;
1277 while (epplit!=eppl.end()) {
1278 if (*epplit == element) {
1285 GINAC_ASSERT(erased);
1287 GINAC_ASSERT(erased);
1290 void expairseq::move_hashtab_entry(epvector::const_iterator oldpos,
1291 epvector::iterator newpos)
1293 GINAC_ASSERT(hashtabsize!=0);
1295 // calculate hashindex of element which was moved
1296 unsigned hashindex=calc_hashindex((*newpos).rest);
1298 // find it in hashtab and modify it
1299 epplist &eppl = hashtab[hashindex];
1300 epplist::iterator epplit = eppl.begin();
1301 while (epplit!=eppl.end()) {
1302 if (*epplit == oldpos) {
1308 GINAC_ASSERT(epplit!=eppl.end());
1311 void expairseq::sorted_insert(epplist &eppl, epvector::const_iterator elem)
1313 epplist::const_iterator current = eppl.begin();
1314 while ((current!=eppl.end()) && ((*current)->is_less(*elem))) {
1317 eppl.insert(current,elem);
1320 void expairseq::build_hashtab_and_combine(epvector::iterator &first_numeric,
1321 epvector::iterator &last_non_zero,
1322 std::vector<bool> &touched,
1323 unsigned &number_of_zeroes)
1325 epp current = seq.begin();
1327 while (current!=first_numeric) {
1328 if (is_exactly_a<numeric>(current->rest)) {
1330 iter_swap(current,first_numeric);
1332 // calculate hashindex
1333 unsigned currenthashindex = calc_hashindex(current->rest);
1335 // test if there is already a matching expair in the hashtab-list
1336 epplist &eppl=hashtab[currenthashindex];
1337 epplist::iterator epplit = eppl.begin();
1338 while (epplit!=eppl.end()) {
1339 if (current->rest.is_equal((*epplit)->rest))
1343 if (epplit==eppl.end()) {
1344 // no matching expair found, append this to end of list
1345 sorted_insert(eppl,current);
1348 // epplit points to a matching expair, combine it with current
1349 (*epplit)->coeff = ex_to<numeric>((*epplit)->coeff).
1350 add_dyn(ex_to<numeric>(current->coeff));
1352 // move obsolete current expair to end by swapping with last_non_zero element
1353 // if this was a numeric, it is swapped with the expair before first_numeric
1354 iter_swap(current,last_non_zero);
1356 if (first_numeric!=last_non_zero) iter_swap(first_numeric,current);
1359 // test if combined term has coeff 0 and can be removed is done later
1360 touched[(*epplit)-seq.begin()] = true;
1366 void expairseq::drop_coeff_0_terms(epvector::iterator &first_numeric,
1367 epvector::iterator &last_non_zero,
1368 std::vector<bool> &touched,
1369 unsigned &number_of_zeroes)
1371 // move terms with coeff 0 to end and remove them from hashtab
1372 // check only those elements which have been touched
1373 epp current = seq.begin();
1375 while (current!=first_numeric) {
1379 } else if (!ex_to<numeric>((*current).coeff).is_zero()) {
1383 remove_hashtab_entry(current);
1385 // move element to the end, unless it is already at the end
1386 if (current!=last_non_zero) {
1387 iter_swap(current,last_non_zero);
1389 bool numeric_swapped = first_numeric!=last_non_zero;
1390 if (numeric_swapped)
1391 iter_swap(first_numeric,current);
1392 epvector::iterator changed_entry;
1394 if (numeric_swapped)
1395 changed_entry = first_numeric;
1397 changed_entry = last_non_zero;
1402 if (first_numeric!=current) {
1404 // change entry in hashtab which referred to first_numeric or last_non_zero to current
1405 move_hashtab_entry(changed_entry,current);
1406 touched[current-seq.begin()] = touched[changed_entry-seq.begin()];
1415 GINAC_ASSERT(i==current-seq.begin());
1418 /** True if one of the coeffs vanishes, otherwise false.
1419 * This would be an invariant violation, so this should only be used for
1420 * debugging purposes. */
1421 bool expairseq::has_coeff_0() const
1423 epvector::const_iterator i = seq.begin(), end = seq.end();
1425 if (i->coeff.is_zero())
1432 void expairseq::add_numerics_to_hashtab(epvector::iterator first_numeric,
1433 epvector::const_iterator last_non_zero)
1435 if (first_numeric == seq.end()) return; // no numerics
1437 epvector::const_iterator current = first_numeric, last = last_non_zero + 1;
1438 while (current != last) {
1439 sorted_insert(hashtab[hashmask], current);
1444 void expairseq::combine_same_terms()
1446 // combine same terms, drop term with coeff 0, move numerics to end
1448 // calculate size of hashtab
1449 hashtabsize = calc_hashtabsize(seq.size());
1451 // hashtabsize is a power of 2
1452 hashmask = hashtabsize-1;
1456 hashtab.resize(hashtabsize);
1458 if (hashtabsize==0) {
1460 combine_same_terms_sorted_seq();
1461 GINAC_ASSERT(!has_coeff_0());
1465 // iterate through seq, move numerics to end,
1466 // fill hashtab and combine same terms
1467 epvector::iterator first_numeric = seq.end();
1468 epvector::iterator last_non_zero = seq.end()-1;
1470 size_t num = seq.size();
1471 std::vector<bool> touched(num);
1473 unsigned number_of_zeroes = 0;
1475 GINAC_ASSERT(!has_coeff_0());
1476 build_hashtab_and_combine(first_numeric,last_non_zero,touched,number_of_zeroes);
1478 // there should not be any terms with coeff 0 from the beginning,
1479 // so it should be safe to skip this step
1480 if (number_of_zeroes!=0) {
1481 drop_coeff_0_terms(first_numeric,last_non_zero,touched,number_of_zeroes);
1484 add_numerics_to_hashtab(first_numeric,last_non_zero);
1486 // pop zero elements
1487 for (unsigned i=0; i<number_of_zeroes; ++i) {
1491 // shrink hashtabsize to calculated value
1492 GINAC_ASSERT(!has_coeff_0());
1496 GINAC_ASSERT(!has_coeff_0());
1499 #endif // EXPAIRSEQ_USE_HASHTAB
1501 /** Check if this expairseq is in sorted (canonical) form. Useful mainly for
1502 * debugging or in assertions since being sorted is an invariance. */
1503 bool expairseq::is_canonical() const
1505 if (seq.size() <= 1)
1508 #if EXPAIRSEQ_USE_HASHTAB
1509 if (hashtabsize > 0) return 1; // not canoncalized
1510 #endif // EXPAIRSEQ_USE_HASHTAB
1512 epvector::const_iterator it = seq.begin(), itend = seq.end();
1513 epvector::const_iterator it_last = it;
1514 for (++it; it!=itend; it_last=it, ++it) {
1515 if (!(it_last->is_less(*it) || it_last->is_equal(*it))) {
1516 if (!is_exactly_a<numeric>(it_last->rest) ||
1517 !is_exactly_a<numeric>(it->rest)) {
1518 // double test makes it easier to set a breakpoint...
1519 if (!is_exactly_a<numeric>(it_last->rest) ||
1520 !is_exactly_a<numeric>(it->rest)) {
1521 printpair(std::clog, *it_last, 0);
1523 printpair(std::clog, *it, 0);
1525 std::clog << "pair1:" << std::endl;
1526 it_last->rest.print(print_tree(std::clog));
1527 it_last->coeff.print(print_tree(std::clog));
1528 std::clog << "pair2:" << std::endl;
1529 it->rest.print(print_tree(std::clog));
1530 it->coeff.print(print_tree(std::clog));
1540 /** Member-wise expand the expairs in this sequence.
1542 * @see expairseq::expand()
1543 * @return pointer to epvector containing expanded pairs or zero pointer,
1544 * if no members were changed. */
1545 std::auto_ptr<epvector> expairseq::expandchildren(unsigned options) const
1547 const epvector::const_iterator last = seq.end();
1548 epvector::const_iterator cit = seq.begin();
1550 const ex &expanded_ex = cit->rest.expand(options);
1551 if (!are_ex_trivially_equal(cit->rest,expanded_ex)) {
1553 // something changed, copy seq, eval and return it
1554 std::auto_ptr<epvector> s(new epvector);
1555 s->reserve(seq.size());
1557 // copy parts of seq which are known not to have changed
1558 epvector::const_iterator cit2 = seq.begin();
1560 s->push_back(*cit2);
1564 // copy first changed element
1565 s->push_back(combine_ex_with_coeff_to_pair(expanded_ex,
1570 while (cit2!=last) {
1571 s->push_back(combine_ex_with_coeff_to_pair(cit2->rest.expand(options),
1580 return std::auto_ptr<epvector>(0); // signalling nothing has changed
1584 /** Member-wise evaluate the expairs in this sequence.
1586 * @see expairseq::eval()
1587 * @return pointer to epvector containing evaluated pairs or zero pointer,
1588 * if no members were changed. */
1589 std::auto_ptr<epvector> expairseq::evalchildren(int level) const
1591 // returns a NULL pointer if nothing had to be evaluated
1592 // returns a pointer to a newly created epvector otherwise
1593 // (which has to be deleted somewhere else)
1596 return std::auto_ptr<epvector>(0);
1598 if (level == -max_recursion_level)
1599 throw(std::runtime_error("max recursion level reached"));
1602 epvector::const_iterator last = seq.end();
1603 epvector::const_iterator cit = seq.begin();
1605 const ex &evaled_ex = cit->rest.eval(level);
1606 if (!are_ex_trivially_equal(cit->rest,evaled_ex)) {
1608 // something changed, copy seq, eval and return it
1609 std::auto_ptr<epvector> s(new epvector);
1610 s->reserve(seq.size());
1612 // copy parts of seq which are known not to have changed
1613 epvector::const_iterator cit2=seq.begin();
1615 s->push_back(*cit2);
1619 // copy first changed element
1620 s->push_back(combine_ex_with_coeff_to_pair(evaled_ex,
1625 while (cit2!=last) {
1626 s->push_back(combine_ex_with_coeff_to_pair(cit2->rest.eval(level),
1635 return std::auto_ptr<epvector>(0); // signalling nothing has changed
1638 /** Member-wise substitute in this sequence.
1640 * @see expairseq::subs()
1641 * @return pointer to epvector containing pairs after application of subs,
1642 * or NULL pointer if no members were changed. */
1643 std::auto_ptr<epvector> expairseq::subschildren(const exmap & m, unsigned options) const
1645 // When any of the objects to be substituted is a product or power
1646 // we have to recombine the pairs because the numeric coefficients may
1647 // be part of the search pattern.
1648 if (!(options & (subs_options::pattern_is_product | subs_options::pattern_is_not_product))) {
1650 // Search the list of substitutions and cache our findings
1651 for (exmap::const_iterator it = m.begin(); it != m.end(); ++it) {
1652 if (is_exactly_a<mul>(it->first) || is_exactly_a<power>(it->first)) {
1653 options |= subs_options::pattern_is_product;
1657 if (!(options & subs_options::pattern_is_product))
1658 options |= subs_options::pattern_is_not_product;
1661 if (options & subs_options::pattern_is_product) {
1663 // Substitute in the recombined pairs
1664 epvector::const_iterator cit = seq.begin(), last = seq.end();
1665 while (cit != last) {
1667 const ex &orig_ex = recombine_pair_to_ex(*cit);
1668 const ex &subsed_ex = orig_ex.subs(m, options);
1669 if (!are_ex_trivially_equal(orig_ex, subsed_ex)) {
1671 // Something changed, copy seq, subs and return it
1672 std::auto_ptr<epvector> s(new epvector);
1673 s->reserve(seq.size());
1675 // Copy parts of seq which are known not to have changed
1676 s->insert(s->begin(), seq.begin(), cit);
1678 // Copy first changed element
1679 s->push_back(split_ex_to_pair(subsed_ex));
1683 while (cit != last) {
1684 s->push_back(split_ex_to_pair(recombine_pair_to_ex(*cit).subs(m, options)));
1695 // Substitute only in the "rest" part of the pairs
1696 epvector::const_iterator cit = seq.begin(), last = seq.end();
1697 while (cit != last) {
1699 const ex &subsed_ex = cit->rest.subs(m, options);
1700 if (!are_ex_trivially_equal(cit->rest, subsed_ex)) {
1702 // Something changed, copy seq, subs and return it
1703 std::auto_ptr<epvector> s(new epvector);
1704 s->reserve(seq.size());
1706 // Copy parts of seq which are known not to have changed
1707 s->insert(s->begin(), seq.begin(), cit);
1709 // Copy first changed element
1710 s->push_back(combine_ex_with_coeff_to_pair(subsed_ex, cit->coeff));
1714 while (cit != last) {
1715 s->push_back(combine_ex_with_coeff_to_pair(cit->rest.subs(m, options), cit->coeff));
1725 // Nothing has changed
1726 return std::auto_ptr<epvector>(0);
1730 // static member variables
1733 #if EXPAIRSEQ_USE_HASHTAB
1734 unsigned expairseq::maxhashtabsize = 0x4000000U;
1735 unsigned expairseq::minhashtabsize = 0x1000U;
1736 unsigned expairseq::hashtabfactor = 1;
1737 #endif // EXPAIRSEQ_USE_HASHTAB
1739 } // namespace GiNaC