#include "Cleanup.hh" #include "Functional.hh" #include "algorithms/factor_out.hh" #include "algorithms/sort_product.hh" #include using namespace cadabra; factor_out::factor_out(const Kernel& k, Ex& e, Ex& args, bool right) : Algorithm(k, e), to_right(right) { cadabra::do_list(args, args.begin(), [&](Ex::iterator arg) { to_factor_out.push_back(Ex(arg)); return true; } ); } /// Check if the expression is a sum with more than one term bool factor_out::can_apply(iterator st) { if(*st->name=="\\sum") return true; return false; } Algorithm::result_t factor_out::apply(iterator& it) { result_t result=result_t::l_no_action; // For every term in the sum, we look at the factors in the product // (or at the single object if there is no product). If this factor // needs to be factored out, we determine if it can be moved all the // way to the left of the expression. If so, move the object to // a 'factored_out' temporary, and take out of the tree. Rinse/repeat. // What's left at the end is two objects: the stuff factored out, // and the rest. Look up if we already have 'the stuff factored out'. // If not, create new. If so, add this term. Ex_comparator comparator(kernel.properties); typedef std::pair > new_term_t; std::vector new_terms; auto term=tr.begin(it); while(term!=tr.end(it)) { auto next_term=term; ++next_term; iterator prod=term; prod_wrap_single_term(prod); Ex collector("\\prod"); // collect all factors that we have taken out // Insert a dummy symbol at the very front or back. // FIXME: there is now a 'can_move_to_front', use that. iterator dummy; if(to_right) dummy = tr.append_child(prod, str_node("dummy")); else dummy = tr.prepend_child(prod, str_node("dummy")); // Look at all factors in turn and determine if they should be taken out. if(to_right) { auto fac=tr.end(prod); auto next=fac; --next; do { fac=next; --next; for(size_t i=0; imultiplier, sign); next=tr.erase(fac); result=result_t::l_applied; break; } } } } while(fac!=tr.begin(prod)); } else { auto fac=tr.begin(prod); while(fac!=tr.end(prod)) { auto next=fac; ++next; for(size_t i=0; imultiplier, sign); next=tr.erase(fac); result=result_t::l_applied; break; } } } fac=next; } } tr.erase(dummy); if(tr.number_of_children(prod)==0) tr.append_child(prod, str_node("1")); // std::cerr << "product after factoring out " << Ex(prod) << std::endl; if(collector.number_of_children(collector.begin())!=0) { // The stuff factored out of this term is in 'collector'. See if we have // factored out that thing before. Because we may not always have collected // factors in the same order (the original expression may not have had // its product sorted), we first sort the collector product. sort_product sp(kernel, collector); sp.dont_cleanup(); // otherwise single-factor products will get stripped of the \prod wrapper. auto coltop=collector.begin(); if(sp.can_apply(coltop)) { sp.apply(coltop); } multiply(prod->multiplier, *coltop->multiplier); one(coltop->multiplier); // Scan through the things factored out so far. bool found=false; for(auto& nt: new_terms) { if(nt.first==collector) { // have that factored out already, add the other factors nt.second.push_back(Ex(prod)); found=true; break; } } // We hadn't factored this bit out before, make a new term. if(!found) { std::vector v; v.push_back(Ex(prod)); new_term_t nt(collector, v); new_terms.push_back(nt); } // All info is now in new_terms; can remove the original. tr.erase(prod); } else { prod_unwrap_single_term(prod); } term=next_term; } // Everything has been collected now into new_terms. Expand those out // into a proper sum of products. for(auto& nt: new_terms) { auto prod = tr.append_child(it, nt.first.begin()); if(nt.second.size()==1) { // factored, but only one term found. auto top = nt.second[0].begin(); // prod node if(to_right) { auto ins = tr.end(top); --ins; while(tr.is_valid(ins)) { tr.prepend_child(prod, iterator(ins)); --ins; } } else { auto ins = tr.begin(top); while(ins!=tr.end(top)) { tr.append_child(prod, iterator(ins)); ++ins; } } multiply(prod->multiplier, *(nt.second[0].begin()->multiplier)); // FIXME: append_children has a BUG! Messes up the tree. But it is needed to // handle terms where the sub-factor is not a simple element. // tr.append_children(prod, nt.second[0].begin(top), nt.second[0].end(top)); cleanup_dispatch(kernel, tr, prod); } else { iterator sum; if(to_right) sum = tr.prepend_child(prod, str_node("\\sum")); else sum = tr.append_child(prod, str_node("\\sum")); for(auto& term: nt.second) { auto tmp = tr.append_child(sum, term.begin()); cleanup_dispatch(kernel, tr, tmp); } } } // std::cerr << "end of factor_out: \n" << Ex(it) << std::endl; return result; }