+void expairseq::construct_from_2_ex_via_exvector(const ex &lh, const ex &rh)
+{
+ exvector v;
+ v.reserve(2);
+ v.push_back(lh);
+ v.push_back(rh);
+ construct_from_exvector(v);
+#if EXPAIRSEQ_USE_HASHTAB
+ GINAC_ASSERT((hashtabsize==0)||(hashtabsize>=minhashtabsize));
+ GINAC_ASSERT(hashtabsize==calc_hashtabsize(seq.size()));
+#endif // EXPAIRSEQ_USE_HASHTAB
+}
+
+void expairseq::construct_from_2_ex(const ex &lh, const ex &rh)
+{
+ if (ex_to<basic>(lh).tinfo()==this->tinfo()) {
+ if (ex_to<basic>(rh).tinfo()==this->tinfo()) {
+#if EXPAIRSEQ_USE_HASHTAB
+ unsigned totalsize = ex_to<expairseq>(lh).seq.size() +
+ ex_to<expairseq>(rh).seq.size();
+ if (calc_hashtabsize(totalsize)!=0) {
+ construct_from_2_ex_via_exvector(lh,rh);
+ } else {
+#endif // EXPAIRSEQ_USE_HASHTAB
+ construct_from_2_expairseq(ex_to<expairseq>(lh),
+ ex_to<expairseq>(rh));
+#if EXPAIRSEQ_USE_HASHTAB
+ }
+#endif // EXPAIRSEQ_USE_HASHTAB
+ return;
+ } else {
+#if EXPAIRSEQ_USE_HASHTAB
+ unsigned totalsize = ex_to<expairseq>(lh).seq.size()+1;
+ if (calc_hashtabsize(totalsize)!=0) {
+ construct_from_2_ex_via_exvector(lh, rh);
+ } else {
+#endif // EXPAIRSEQ_USE_HASHTAB
+ construct_from_expairseq_ex(ex_to<expairseq>(lh), rh);
+#if EXPAIRSEQ_USE_HASHTAB
+ }
+#endif // EXPAIRSEQ_USE_HASHTAB
+ return;
+ }
+ } else if (ex_to<basic>(rh).tinfo()==this->tinfo()) {
+#if EXPAIRSEQ_USE_HASHTAB
+ unsigned totalsize=ex_to<expairseq>(rh).seq.size()+1;
+ if (calc_hashtabsize(totalsize)!=0) {
+ construct_from_2_ex_via_exvector(lh,rh);
+ } else {
+#endif // EXPAIRSEQ_USE_HASHTAB
+ construct_from_expairseq_ex(ex_to<expairseq>(rh),lh);
+#if EXPAIRSEQ_USE_HASHTAB
+ }
+#endif // EXPAIRSEQ_USE_HASHTAB
+ return;
+ }
+
+#if EXPAIRSEQ_USE_HASHTAB
+ if (calc_hashtabsize(2)!=0) {
+ construct_from_2_ex_via_exvector(lh,rh);
+ return;
+ }
+ hashtabsize = 0;
+#endif // EXPAIRSEQ_USE_HASHTAB
+
+ if (is_exactly_a<numeric>(lh)) {
+ if (is_exactly_a<numeric>(rh)) {
+ combine_overall_coeff(lh);
+ combine_overall_coeff(rh);
+ } else {
+ combine_overall_coeff(lh);
+ seq.push_back(split_ex_to_pair(rh));
+ }
+ } else {
+ if (is_exactly_a<numeric>(rh)) {
+ combine_overall_coeff(rh);
+ seq.push_back(split_ex_to_pair(lh));
+ } else {
+ expair p1 = split_ex_to_pair(lh);
+ expair p2 = split_ex_to_pair(rh);
+
+ int cmpval = p1.rest.compare(p2.rest);
+ if (cmpval==0) {
+ p1.coeff = ex_to<numeric>(p1.coeff).add_dyn(ex_to<numeric>(p2.coeff));
+ if (!ex_to<numeric>(p1.coeff).is_zero()) {
+ // no further processing is necessary, since this
+ // one element will usually be recombined in eval()
+ seq.push_back(p1);
+ }
+ } else {
+ seq.reserve(2);
+ if (cmpval<0) {
+ seq.push_back(p1);
+ seq.push_back(p2);
+ } else {
+ seq.push_back(p2);
+ seq.push_back(p1);
+ }
+ }
+ }
+ }
+}
+
+void expairseq::construct_from_2_expairseq(const expairseq &s1,
+ const expairseq &s2)
+{
+ combine_overall_coeff(s1.overall_coeff);
+ combine_overall_coeff(s2.overall_coeff);
+
+ epvector::const_iterator first1 = s1.seq.begin();
+ epvector::const_iterator last1 = s1.seq.end();
+ epvector::const_iterator first2 = s2.seq.begin();
+ epvector::const_iterator last2 = s2.seq.end();
+
+ seq.reserve(s1.seq.size()+s2.seq.size());
+
+ bool needs_further_processing=false;
+
+ while (first1!=last1 && first2!=last2) {
+ int cmpval = (*first1).rest.compare((*first2).rest);
+ if (cmpval==0) {
+ // combine terms
+ const numeric &newcoeff = ex_to<numeric>(first1->coeff).
+ add(ex_to<numeric>(first2->coeff));
+ if (!newcoeff.is_zero()) {
+ seq.push_back(expair(first1->rest,newcoeff));
+ if (expair_needs_further_processing(seq.end()-1)) {
+ needs_further_processing = true;
+ }
+ }
+ ++first1;
+ ++first2;
+ } else if (cmpval<0) {
+ seq.push_back(*first1);
+ ++first1;
+ } else {
+ seq.push_back(*first2);
+ ++first2;
+ }
+ }
+
+ while (first1!=last1) {
+ seq.push_back(*first1);
+ ++first1;
+ }
+ while (first2!=last2) {
+ seq.push_back(*first2);
+ ++first2;
+ }
+
+ if (needs_further_processing) {
+ epvector v = seq;
+ seq.clear();
+ construct_from_epvector(v);
+ }
+}
+
+void expairseq::construct_from_expairseq_ex(const expairseq &s,
+ const ex &e)
+{
+ combine_overall_coeff(s.overall_coeff);
+ if (is_exactly_a<numeric>(e)) {
+ combine_overall_coeff(e);
+ seq = s.seq;
+ return;
+ }
+
+ epvector::const_iterator first = s.seq.begin();
+ epvector::const_iterator last = s.seq.end();
+ expair p = split_ex_to_pair(e);
+
+ seq.reserve(s.seq.size()+1);
+ bool p_pushed = false;
+
+ bool needs_further_processing=false;
+
+ // merge p into s.seq
+ while (first!=last) {
+ int cmpval = (*first).rest.compare(p.rest);
+ if (cmpval==0) {
+ // combine terms
+ const numeric &newcoeff = ex_to<numeric>(first->coeff).
+ add(ex_to<numeric>(p.coeff));
+ if (!newcoeff.is_zero()) {
+ seq.push_back(expair(first->rest,newcoeff));
+ if (expair_needs_further_processing(seq.end()-1))
+ needs_further_processing = true;
+ }
+ ++first;
+ p_pushed = true;
+ break;
+ } else if (cmpval<0) {
+ seq.push_back(*first);
+ ++first;
+ } else {
+ seq.push_back(p);
+ p_pushed = true;
+ break;
+ }
+ }
+
+ if (p_pushed) {
+ // while loop exited because p was pushed, now push rest of s.seq
+ while (first!=last) {
+ seq.push_back(*first);
+ ++first;
+ }
+ } else {
+ // while loop exited because s.seq was pushed, now push p
+ seq.push_back(p);
+ }
+
+ if (needs_further_processing) {
+ epvector v = seq;
+ seq.clear();
+ construct_from_epvector(v);
+ }
+}
+
+void expairseq::construct_from_exvector(const exvector &v)
+{
+ // simplifications: +(a,+(b,c),d) -> +(a,b,c,d) (associativity)
+ // +(d,b,c,a) -> +(a,b,c,d) (canonicalization)
+ // +(...,x,*(x,c1),*(x,c2)) -> +(...,*(x,1+c1+c2)) (c1, c2 numeric())
+ // (same for (+,*) -> (*,^)
+
+ make_flat(v);
+#if EXPAIRSEQ_USE_HASHTAB
+ combine_same_terms();
+#else
+ canonicalize();
+ combine_same_terms_sorted_seq();
+#endif // EXPAIRSEQ_USE_HASHTAB