+
+ for (const auto & cit : expanded_seq) {
+ if (is_exactly_a<add>(cit.rest) &&
+ (cit.coeff.is_equal(_ex1))) {
+ if (is_exactly_a<add>(last_expanded)) {
+
+ // Expand a product of two sums, aggressive version.
+ // Caring for the overall coefficients in separate loops can
+ // sometimes give a performance gain of up to 15%!
+
+ const int sizedifference = ex_to<add>(last_expanded).seq.size()-ex_to<add>(cit.rest).seq.size();
+ // add2 is for the inner loop and should be the bigger of the two sums
+ // in the presence of asymptotically good sorting:
+ const add& add1 = (sizedifference<0 ? ex_to<add>(last_expanded) : ex_to<add>(cit.rest));
+ const add& add2 = (sizedifference<0 ? ex_to<add>(cit.rest) : ex_to<add>(last_expanded));
+ epvector distrseq;
+ distrseq.reserve(add1.seq.size()+add2.seq.size());
+
+ // Multiply add2 with the overall coefficient of add1 and append it to distrseq:
+ if (!add1.overall_coeff.is_zero()) {
+ if (add1.overall_coeff.is_equal(_ex1))
+ distrseq.insert(distrseq.end(), add2.seq.begin(), add2.seq.end());
+ else
+ for (const auto & i : add2.seq)
+ distrseq.push_back(expair(i.rest, ex_to<numeric>(i.coeff).mul_dyn(ex_to<numeric>(add1.overall_coeff))));
+ }
+
+ // Multiply add1 with the overall coefficient of add2 and append it to distrseq:
+ if (!add2.overall_coeff.is_zero()) {
+ if (add2.overall_coeff.is_equal(_ex1))
+ distrseq.insert(distrseq.end(), add1.seq.begin(), add1.seq.end());
+ else
+ for (const auto & i : add1.seq)
+ distrseq.push_back(expair(i.rest, ex_to<numeric>(i.coeff).mul_dyn(ex_to<numeric>(add2.overall_coeff))));
+ }
+
+ // Compute the new overall coefficient and put it together:
+ ex tmp_accu = dynallocate<add>(distrseq, add1.overall_coeff*add2.overall_coeff);
+
+ exvector add1_dummy_indices, add2_dummy_indices, add_indices;
+ lst dummy_subs;
+
+ if (!skip_idx_rename) {
+ for (const auto & i : add1.seq) {
+ add_indices = get_all_dummy_indices_safely(i.rest);
+ add1_dummy_indices.insert(add1_dummy_indices.end(), add_indices.begin(), add_indices.end());
+ }
+ for (const auto & i : add2.seq) {
+ add_indices = get_all_dummy_indices_safely(i.rest);
+ add2_dummy_indices.insert(add2_dummy_indices.end(), add_indices.begin(), add_indices.end());
+ }
+
+ sort(add1_dummy_indices.begin(), add1_dummy_indices.end(), ex_is_less());
+ sort(add2_dummy_indices.begin(), add2_dummy_indices.end(), ex_is_less());
+ dummy_subs = rename_dummy_indices_uniquely(add1_dummy_indices, add2_dummy_indices);
+ }
+
+ // Multiply explicitly all non-numeric terms of add1 and add2:
+ for (const auto & i2 : add2.seq) {
+ // We really have to combine terms here in order to compactify
+ // the result. Otherwise it would become waayy tooo bigg.
+ numeric oc(*_num0_p);
+ epvector distrseq2;
+ distrseq2.reserve(add1.seq.size());
+ const ex i2_new = (skip_idx_rename || (dummy_subs.op(0).nops() == 0) ?
+ i2.rest :
+ i2.rest.subs(ex_to<lst>(dummy_subs.op(0)),
+ ex_to<lst>(dummy_subs.op(1)), subs_options::no_pattern));
+ for (const auto & i1 : add1.seq) {
+ // Don't push_back expairs which might have a rest that evaluates to a numeric,
+ // since that would violate an invariant of expairseq:
+ const ex rest = dynallocate<mul>(i1.rest, i2_new);
+ if (is_exactly_a<numeric>(rest)) {
+ oc += ex_to<numeric>(rest).mul(ex_to<numeric>(i1.coeff).mul(ex_to<numeric>(i2.coeff)));
+ } else {
+ distrseq2.push_back(expair(rest, ex_to<numeric>(i1.coeff).mul_dyn(ex_to<numeric>(i2.coeff))));
+ }