#include "Cleanup.hh" #include "Functional.hh" #include "Algorithm.hh" #include "algorithms/collect_terms.hh" #include "properties/Coordinate.hh" #include "properties/SelfAntiCommuting.hh" #include "properties/Integer.hh" #include "properties/Diagonal.hh" #include "properties/ExteriorDerivative.hh" #include "properties/DifferentialForm.hh" #include "properties/KroneckerDelta.hh" #include "properties/Matrix.hh" #include "properties/NumericalFlat.hh" #include "properties/PartialDerivative.hh" #include "properties/ImaginaryI.hh" // #define DEBUG 1 namespace cadabra { void cleanup_dispatch(const Kernel& kernel, Ex& tr, Ex::iterator& it) { #ifdef DEBUG std::cerr << "cleanup at " << *it->name << std::endl; #endif // Run the cleanup as long as the expression changes. bool changed; do { changed=false; bool res=false; if(it->is_zero() && (tr.number_of_children(it)!=0 || *it->name!="1")) { cadabra::zero(it->multiplier); tr.erase_children(it); it->name=name_set.insert("1").first; // once we hit zero, there is nothing to simplify anymore break; } if(*it->name=="\\frac") res = cleanup_fraclike(kernel, tr, it); changed = changed || res; if(*it->name=="\\pow") res = cleanup_powlike(kernel, tr, it); changed = changed || res; if(*it->name=="\\prod" || *it->name=="\\wedge") res = cleanup_productlike(kernel, tr, it); changed = changed || res; if(*it->name=="\\sum") res = cleanup_sumlike(kernel, tr, it); changed = changed || res; if(*it->name=="\\comma") res = cleanup_comma(kernel, tr, it); changed = changed || res; if(*it->name=="\\tie") res = cleanup_tie(kernel, tr, it); changed = changed || res; if(*it->name=="\\components") res = cleanup_components(kernel, tr, it); changed = changed || res; const Derivative *der = kernel.properties.get(it); if(der) { res = cleanup_derivative(kernel, tr, it); changed = changed || res; } const PartialDerivative *pder = kernel.properties.get(it); if(pder) { res = cleanup_partialderivative(kernel, tr, it); changed = changed || res; } // std::cerr << "derivative " << changed << std::endl; const NumericalFlat *nf = kernel.properties.get(it); if(nf) { res = cleanup_numericalflat(kernel, tr, it); changed = changed || res; } // std::cerr << "numerical " << changed << std::endl; const Diagonal *diag = kernel.properties.get(it); if(diag) { res = cleanup_diagonal(kernel, tr, it); changed = changed || res; } // std::cerr << "diagonal " << changed << std::endl; //std::cerr << "Is symbol " << Ex(it) << " a KD?" << std::endl; const KroneckerDelta *kr = kernel.properties.get(it); if(kr) { //std::cerr << "Symbol " << Ex(it) << " is a KD" << std::endl; res = cleanup_kronecker(kernel, tr, it); changed = changed || res; } // std::cerr << "delta " << changed << std::endl; const ExteriorDerivative *ed = kernel.properties.get(it); if(ed) { res = cleanup_exterior_derivative(kernel, tr, it); changed = changed || res; } } while(changed); // std::cerr << Ex(it) << std::endl; } void check_index_consistency(const Kernel& k, Ex& tr, Ex::iterator it) { if(it==tr.end()) return; collect_terms ct(k, tr); ct.check_index_consistency(it); ct.check_degree_consistency(it); // FIXME: needs to be implemented in Algorithm. } bool cleanup_fraclike(const Kernel& k, Ex&tr, Ex::iterator& it) { auto arg=tr.begin(it); if(*arg->name=="\\equals") { // When dividing an equation by something else, divide both sides. auto div=arg; ++div; auto lhs=tr.begin(arg); auto rhs=lhs; rhs.skip_children(); ++rhs; auto lhsfrac=tr.wrap(lhs, str_node("\\frac")); auto rhsfrac=tr.wrap(rhs, str_node("\\frac")); tr.append_child(lhsfrac, div); tr.append_child(rhsfrac, div); it=tr.flatten_and_erase(it); return true; } return false; } bool cleanup_powlike(const Kernel& k, Ex&tr, Ex::iterator& it) { auto arg=tr.begin(it); auto exp=arg; ++exp; if(exp==tr.end(it)) return false; // (anything)^1 = anything if(exp->is_integer() && *exp->multiplier==1) { tr.erase(exp); it = tr.flatten_and_erase(it); return true; } if(*arg->name=="1") { if(*arg->multiplier==0) { // 0**anything = 0 zero(it->multiplier); return true; } if(*arg->multiplier==1) { // 1**anything = 1 one(it->multiplier); tr.erase_children(it); it->name=name_set.insert("1").first; return true; } if(*exp->name=="1" && *exp->multiplier==-1) { // Turn (numerical)**(-1) into a multiplier. multiply(it->multiplier, multiplier_t(1)/(*arg->multiplier)); tr.erase_children(it); it->name = name_set.insert("1").first; return true; } } // Turn \pow{mA A}{B} with mA the multiplier for A into mA^B \pow{A}{B} if(exp->is_integer() && *arg->multiplier!=1 && *arg->name!="1") { mpz_class nw_n, nw_d; // std::cerr << "also doing " << arg << ", " << *arg->multiplier << "**" << *exp->multiplier << "***" << std::endl; long Cexp=to_long(*exp->multiplier); mpz_pow_ui(nw_n.get_mpz_t(), arg->multiplier->get_num().get_mpz_t(), std::abs(Cexp)); mpz_pow_ui(nw_d.get_mpz_t(), arg->multiplier->get_den().get_mpz_t(), std::abs(Cexp)); // std::cerr << nw_n << ", " << nw_d << std::endl; if(Cexp<0) std::swap(nw_n, nw_d); multiplier_t newmult=multiplier_t(nw_n, nw_d); newmult.canonicalize(); // std::cerr << newmult << std::endl; it->multiplier=rat_set.insert(newmult).first; one(arg->multiplier); return true; } // Turn \pow{mult \pow{A}{B}}{C} into \pow{mult}{C} \pow{A}{B*C} if C is an integer. // A bit tricky with the multiplier of \pow{A}{B}, as that becomes mult^C // and can then either be absorbed into the overall multiplier, or needs // a second factor. auto ipow=tr.begin(it); if(*ipow->name=="\\pow") { // std::cerr << "*POW" << std::endl; // tr.print_recursive_treeform(std::cerr, it); auto iA=tr.begin(ipow); auto iB=iA; ++iB; auto iC=ipow; ++iC; // std::cerr << it << std::endl; if(iC->is_integer() || k.properties.get(iC)) { if(iC->is_integer()) { // newmult = (mult)^C; mpz_class nw_n, nw_d; // std::cerr << "doing " << *ipow->multiplier << "**" << *iC->multiplier << std::endl; long Cexp=to_long(*iC->multiplier); mpz_pow_ui(nw_n.get_mpz_t(), ipow->multiplier->get_num().get_mpz_t(), std::abs(Cexp)); mpz_pow_ui(nw_d.get_mpz_t(), ipow->multiplier->get_den().get_mpz_t(), std::abs(Cexp)); if(Cexp<0) std::swap(nw_n, nw_d); multiplier_t newmult=multiplier_t(nw_n, nw_d); newmult.canonicalize(); // std::cerr << "new multiplier " << newmult << std::endl; ipow->multiplier=rat_set.insert(newmult).first; } else { // need to generate (mult)^C as a separate factor, if mult!=1. if(*ipow->multiplier!=1) { // std::cerr << "generate separate factor for " << *ipow->multiplier << "**" << iC << std::endl; Ex nw("\\pow"); nw.append_child(nw.begin(), str_node("1"))->multiplier=ipow->multiplier; nw.append_child(nw.begin(), Ex::iterator(iC)); tr.wrap(it, str_node("\\prod")); tr.insert_subtree(it, nw.begin()); one(ipow->multiplier); } } Ex::iterator expprod=tr.wrap(iB, str_node("\\prod")); tr.move_after(iB, iC); it=tr.flatten_and_erase(it); cleanup_productlike(k, tr, expprod); // std::cerr << "after: " << it << std::endl; return true; } } return false; } bool cleanup_productlike(const Kernel& k, Ex&tr, Ex::iterator& it) { bool ret=false; assert(*it->name=="\\prod" || *it->name=="\\wedge"); std::string nm = *it->name; // Flatten prod children inside this prod node. auto sib=tr.begin(it); while(sib!=tr.end(it)) { if(*sib->name==nm) { multiply(it->multiplier, *sib->multiplier); tr.flatten(sib); sib=tr.erase(sib); ret=true; } else ++sib; } if(tr.number_of_children(it)==1) if(tr.begin(it)->is_range_wildcard()) return ret; ret = ret || cleanup_numericalflat(k, tr, it); // Turn products of ImaginaryI into -1 factors. if(tr.number_of_children(it)>1) { std::vector fs; auto sib=tr.begin(it); while(sib!=tr.end(it)) { if(k.properties.get(sib)) fs.push_back(sib); ++sib; } multiplier_t mult=1; for(size_t i=0; imultiplier, mult); } // Turn products with two adjacent identical anti-commuting siblings to zero. if(nm=="\\prod") { auto s1=tr.begin(it); auto s2=s1; ++s2; while(s2!=tr.end(it)) { auto ac = k.properties.get(s1); if(ac) { if(subtree_compare(0, s1, s2)==0) { tr.erase_children(it); zero(it->multiplier); ret=true; break; } } ++s2; ++s1; } } // Turn wedge products containing two identical siblings of odd degree to zero // if they are not matrix objects. if(nm=="\\wedge") { auto s1=tr.begin(it); auto s2=s1; ++s2; while(s2!=tr.end(it)) { if(subtree_compare(0, s1, s2)==0) { auto df1 = k.properties.get(s1); auto df2 = k.properties.get(s2); auto mat1 = k.properties.get(s1); auto mat2 = k.properties.get(s2); if(df1 && df2 && !(mat1 && mat2) ) { auto degree1 = df1->degree(k.properties, s1); auto degree2 = df2->degree(k.properties, s2); if(degree1.is_rational() && degree2.is_rational()) { long d1 = to_long(degree1.to_rational()); long d2 = to_long(degree2.to_rational()); if(d1==d2 && d1%2==1) { tr.erase_children(it); zero(it->multiplier); ret=true; break; } } } } ++s2; ++s1; } } // Handle edge cases where the product should collapse to a single node, // e.g. when we have just a single factor, or when the product vanishes. if(tr.number_of_children(it)==1) { // i.e. from '3*4*7*a*9' ret=true; tr.begin(it)->fl.bracket=it->fl.bracket; tr.begin(it)->fl.parent_rel=it->fl.parent_rel; tr.begin(it)->multiplier=it->multiplier; tr.flatten(it); it=tr.erase(it); push_down_multiplier(k, tr, it); } else if(tr.number_of_children(it)==0) { // i.e. from '3*4*7*9' ret=true; it->name=name_set.insert("1").first; } // If any of the elements is an `\equals`, multiply the rest of the // terms through on both sides. If there is more than one `\equals`, throw // an error. sib=tr.begin(it); Ex::sibling_iterator equals_node=tr.end(it); while(sib!=tr.end(it)) { if(*sib->name=="\\equals") { if(equals_node!=tr.end(it)) throw ConsistencyException("Encountered more than one equalities in a product; undefined."); else equals_node=sib; } ++sib; } if(equals_node!=tr.end(it)) { Ex::sibling_iterator lhs=tr.begin(equals_node); Ex::sibling_iterator rhs=lhs; ++rhs; auto lhsprod=tr.wrap(lhs, str_node("\\prod")); auto rhsprod=tr.wrap(rhs, str_node("\\prod")); sib=tr.begin(it); bool left=true; while(sib!=tr.end(it)) { if(sib!=equals_node) { if(left) { tr.prepend_child(lhsprod, sib); tr.prepend_child(rhsprod, sib); } else { tr.append_child(lhsprod, sib); tr.append_child(rhsprod, sib); } sib=tr.erase(sib); } else { left=false; ++sib; } } Ex::iterator tmp1(lhsprod), tmp2(rhsprod); cleanup_dispatch(k, tr, tmp1); cleanup_dispatch(k, tr, tmp2); it=tr.flatten_and_erase(it); ret=true; } return ret; } bool cleanup_sumlike(const Kernel& k, Ex&tr, Ex::iterator& it) { #ifdef DEBUG std::cerr << "cleanup_sumlike, before: " << it << std::endl; if(tr.number_of_children(it)==0) std::cerr << "zero children on sum; " << it.node << ", tree = " << tr.begin() << std::endl; #endif assert(*it->name=="\\sum"); bool ret=false; // Remove children which are 0 Ex::sibling_iterator sib=tr.begin(it); while(sib!=tr.end(it)) { if(sib->is_zero()) { ret=true; sib=tr.erase(sib); } else ++sib; } // Do not allow equalities as terms inside a sum if there are // other terms present as well. sib=tr.begin(it); int equalities=0; int nonequalities=0; while(sib!=tr.end(it)) { if(*sib->name=="\\equals") ++equalities; else ++nonequalities; if(equalities!=0 && nonequalities!=0) throw ConsistencyException("Encountered an equality and a normal term in the same sum; not allowed."); ++sib; } if(equalities>1) { // This is a sum of at least 2 equalities. // Combine all lhs and all rhs. auto frst=tr.begin(it); Ex::sibling_iterator lhs=tr.begin(frst); Ex::sibling_iterator rhs=lhs; ++rhs; // Ensure both lhs and rhs are sums. if(*lhs->name!="\\sum") lhs=tr.wrap(lhs, str_node("\\sum")); if(*rhs->name!="\\sum") rhs=tr.wrap(rhs, str_node("\\sum")); Ex::sibling_iterator lhsend=tr.end(lhs); Ex::sibling_iterator rhsend=tr.end(rhs); sib=frst; ++sib; while(sib!=tr.end(it)) { // sib is an `\equals` node Ex::sibling_iterator side=tr.begin(sib); multiply(side->multiplier, *sib->multiplier); tr.move_before(lhsend, side); side=tr.begin(sib); multiply(side->multiplier, *sib->multiplier); tr.move_before(rhsend, side); sib=tr.erase(sib); } #ifdef DEBUG std::cerr << "got through equals cleanup" << std::endl; std::cerr << it << std::endl; #endif Ex::iterator tmp1=lhs, tmp2=rhs; cleanup_sumlike(k, tr, tmp1); cleanup_sumlike(k, tr, tmp2); } // Flatten sums which are supposed to be flat. long num=tr.number_of_children(it); if(num==0) { ret=true; cadabra::zero(it->multiplier); return ret; } if(num==1) { if(tr.begin(it)->is_range_wildcard()) return ret; ret=true; multiply(tr.begin(it)->multiplier, *it->multiplier); tr.flatten(it); it=tr.erase(it); } else { auto facs=tr.begin(it); str_node::bracket_t btype_par=facs->fl.bracket; while(facs!=tr.end(it)) { if(facs->fl.bracket!=str_node::b_none) { btype_par=facs->fl.bracket; } ++facs; } facs=tr.begin(it); while(facs!=tr.end(it)) { if(*facs->name=="\\sum") { auto terms=tr.begin(facs); auto tmp=facs; ++tmp; while(terms!=tr.end(facs)) { multiply(terms->multiplier,*facs->multiplier); terms->fl.bracket=btype_par; ++terms; } ret=true; tr.flatten(facs); tr.erase(facs); facs=tmp; } else ++facs; } } ret = ret || push_down_multiplier(k, tr, it); #ifdef DEBUG std::cerr << "cleanup_sumlike, after, " << ret << ": " << it << std::endl; #endif return ret; } bool push_down_multiplier(const Kernel& k, Ex& tr, Ex::iterator it) { bool ret=false; auto mult=*it->multiplier; if(mult==1) return ret; if(*it->name=="\\sum" || *it->name=="\\equals") { auto sib=tr.begin(it); while(sib!=tr.end(it)) { ret=true; multiply(sib->multiplier, mult); push_down_multiplier(k, tr, sib); ++sib; } if(*it->multiplier!=1) ret=true; one(it->multiplier); } else if(*it->name=="\\components") { Ex::sibling_iterator sib=tr.end(it); --sib; // Examine all index value sets and push the multiplier // in there. cadabra::do_list(tr, sib, [&](Ex::iterator nd) { Ex::sibling_iterator val=tr.begin(nd); ++val; if(mult!=1) { ret=true; multiply(val->multiplier, mult); } // Need to evaluate it; just putting it in '||' may lead to the compiler not evaluating it if // 'ret' is already true! bool tmp = push_down_multiplier(k, tr, val); ret = ret || tmp; return true; }); if(*it->multiplier!=1) ret=true; one(it->multiplier); } return ret; } bool cleanup_components(const Kernel& k, Ex&tr, Ex::iterator& it) { assert(*it->name=="\\components"); bool ret=push_down_multiplier(k, tr, it); // If this component node has no free indices, get rid of all // the baggage and turn into a normal expression. // std::cerr << "components cleanup: " << Ex(it) << std::endl; auto comma=tr.begin(it); if(*comma->name=="\\comma") { // If the first child is \comma, there are no indices: scalar. if(tr.number_of_children(comma)==0) { // Totally empty component node, can happen after an // evaluate with no rules matching. zero(it->multiplier); ret=true; return ret; } ret=true; // std::cerr << "components node for a scalar" << std::endl; tr.flatten(comma); // unwrap comma comma=tr.erase(comma); // erase comma tr.flatten(comma); // unwrap equals comma=tr.erase(comma); // erase equals comma=tr.erase(comma); // remove empty comma for index values tr.flatten(it); // remove components node it=tr.erase(it); // std::cerr << Ex(it) << std::endl; } else { while(comma!=tr.end(it)) { if(*comma->name=="\\comma") { if(tr.number_of_children(comma)==0) { ret=true; zero(it->multiplier); } // Still check if there is a component value for which the index // values exactly match the index names. In that case, replace // the entire components node with the component value. auto equals = tr.begin(comma); while(equals != tr.end(comma)) { auto valcomma = tr.begin(equals); auto valindices=tr.begin(valcomma); auto expindices=tr.begin(it); Ex_comparator comp(k.properties); bool foundmatch=true; while(valindices!=tr.end(valcomma)) { auto match = comp.equal_subtree(valindices, expindices, Ex_comparator::useprops_t::not_at_top, true); if(! (match==Ex_comparator::match_t::node_match || match==Ex_comparator::match_t::subtree_match)) { foundmatch=false; break; } ++expindices; ++valindices; } if(foundmatch) { // Yep, we can unwrap this component and replace it with the // single value. auto erase=tr.begin(it); while(erase!=comma) // erase indices from \components erase=tr.erase(erase); auto eit=tr.begin(comma); while(eit!=tr.end(comma)) { // erase all component values which we do not need. if(eit==equals) ++eit; else eit=tr.erase(eit); } tr.flatten(comma); // unwrap comma comma=tr.erase(comma); // erase comma tr.flatten(comma); // unwrap equals comma=tr.erase(comma); // erase equals comma=tr.erase(comma); // remove comma node (plus its children) for index values tr.flatten(it); // remove components node it=tr.erase(it); return true; } ++equals; } // None of the index value sets match the index names. If the index names are // coordinates, this means that the value of this component is zero. auto expindices=tr.begin(it); bool all_coordinates=true; while(*expindices->name!="\\comma") { if(expindices->is_integer()==false && k.properties.get(expindices, true)==0) { all_coordinates=false; break; } ++expindices; } if(all_coordinates) { zero(it->multiplier); return true; } return ret; } ++comma; } // Anonymous tensor with all components vanishing. ret=true; zero(it->multiplier); } return ret; } bool cleanup_partialderivative(const Kernel&, Ex& tr, Ex::iterator& it) { // Nested derivatives with the same name should be flattened, but // only if both the outer derivative and the inner derivative have // an index (otherwise D(D(A)) becomes D(A) which is wrong). // Find first non-index child. bool ret=false; Ex::sibling_iterator sib=tr.begin(it); if(sib==tr.end(it)) return ret; while(sib->is_index()) { ++sib; if(sib==tr.end(it)) { zero(it->multiplier); return true; } if(sib==tr.end(it)) throw ConsistencyException("Encountered PartialDerivative object without argument on which to act."); } // FIXME: this ignores that derivatives can have functional child // nodes which are interpreted as 'object wrt. with derivative should be taken'. if(it->name == sib->name) { if(Algorithm::number_of_direct_indices(it)>0 && Algorithm::number_of_direct_indices(sib)>0) { multiply(it->multiplier, *sib->multiplier); tr.flatten(sib); tr.erase(sib); ret=true; } } return ret; } bool cleanup_derivative(const Kernel& k, Ex& tr, Ex::iterator& it) { bool ret=false; if(Algorithm::number_of_direct_indices(it) == tr.number_of_children(it)) { // This is a derivative acting on nothing, always occurs // when all constants have been moved out. zero(it->multiplier); ret=true; return ret; } auto sib=tr.begin(it); while(sib!=tr.end(it)) { if(sib->fl.parent_rel==str_node::p_none) { if(*sib->name=="\\equals") { // FIXME: this should probably be taken out for generalisation. auto lhs = tr.begin(sib); auto rhs = lhs; ++rhs; auto lhswrap = tr.wrap(lhs, *it); auto rhswrap = tr.wrap(rhs, *it); multiply(lhswrap->multiplier, *it->multiplier); multiply(rhswrap->multiplier, *it->multiplier); auto sib2=tr.begin(it); while(sib2!=tr.end(it)) { if(sib2!=sib) { tr.insert_subtree(lhs, sib2); tr.insert_subtree(rhs, sib2); sib2=tr.erase(sib2); } else ++sib2; } it=tr.flatten(it); it=tr.erase(it); Ex::iterator tmp1(lhswrap), tmp2(rhswrap); cleanup_dispatch(k, tr, tmp1); cleanup_dispatch(k, tr, tmp2); ret=true; break; } } ++sib; } return ret; } bool cleanup_numericalflat(const Kernel&, Ex& tr, Ex::iterator& it) { bool ret=false; //tr.print_recursive_treeform(std::cerr, it); // Collect all multipliers and remove resulting '1' nodes. auto facs=tr.begin(it); multiplier_t factor=1; while(facs!=tr.end(it)) { // std::cerr << "at " << *facs << std::endl; if(facs->is_index()==false) { // Do not collect the number in e.g. \partial_{4}{A}. factor*=*facs->multiplier; if(facs->is_rational()) { multiplier_t tmp; // FIXME: there is a bug in gmp which means we have to put init on next line. tmp=(*facs->name).c_str(); ret=true; factor*=tmp; facs=tr.erase(facs); if(facs==tr.end()) facs=tr.end(it); } else { if(*facs->multiplier!=1) ret=true; one(facs->multiplier); ++facs; } } else ++facs; } if(factor!=1) ret=true; multiply(it->multiplier,factor); return ret; } bool cleanup_diagonal(const Kernel& k, Ex& tr, Ex::iterator& it) { bool ret=false; if(tr.number_of_children(it)!=2) return ret; auto c1=tr.begin(it); auto c2(c1); ++c2; // Two different numerical indices will lead to zero. if(c1->is_rational() && c2->is_rational()) if(c1->multiplier != c2->multiplier) { ret=true; zero(it->multiplier); } // Two different Coordinate indices will lead to zero. if(!(c1->is_rational() && c2->is_rational())) { auto *c1coord = k.properties.get(c1, true); auto *c2coord = k.properties.get(c2, true); if(c1coord!=0 && c2coord!=0) { if(subtree_compare(0, c1, c2)!=0) { ret=true; zero(it->multiplier); } } } return ret; } bool cleanup_kronecker(const Kernel&, Ex& tr, Ex::iterator& it) { bool ret=false; if(tr.number_of_children(it)!=2) return ret; auto c1=tr.begin(it); auto c2(c1); ++c2; if(c1->is_rational() && c2->is_rational()) { if(c1->multiplier != c2->multiplier) { ret=true; zero(it->multiplier); } else { // ::one(it->multiplier); tr.erase_children(it); ret=true; it->name=name_set.insert("1").first; } } return ret; } bool cleanup_exterior_derivative(const Kernel& k, Ex& tr, Ex::iterator& it) { // FIXME: could have this act on a sum as well. if(tr.number_of_children(it)==1) { auto sib=tr.begin(it); const ExteriorDerivative *ed1=k.properties.get(it); const ExteriorDerivative *ed2=k.properties.get(sib); if(ed1==ed2) { zero(it->multiplier); return true; } } return false; } bool cleanup_comma(const Kernel& k, Ex& tr, Ex::iterator& it) { if(*it->multiplier!=1) { Ex::sibling_iterator sib = tr.begin(it); while(sib!=tr.end(it)) { multiply(sib->multiplier, *it->multiplier); ++sib; } one(it->multiplier); return true; } else return false; } bool cleanup_tie(const Kernel& k, Ex& tr, Ex::iterator& it) { // Are all siblings lists? Ex::sibling_iterator sib = tr.begin(it); while(sib!=tr.end(it)) { if(*sib->name!="\\comma") return false; ++sib; } // All siblings are lists. Join them together into one // long list. it->name = name_set.insert("\\comma").first; sib=tr.begin(it); while(sib!=tr.end(it)) { auto nxt = sib; ++nxt; tr.flatten_and_erase(sib); sib=nxt; } return true; } void cleanup_dispatch_deep(const Kernel& k, Ex& tr, dispatcher_t dispatch) { Ex::iterator top=tr.begin(); cleanup_dispatch_deep(k, tr, top, dispatch); } void cleanup_dispatch_deep(const Kernel& k, Ex& tr, Ex::iterator&, dispatcher_t dispatch) { // Cleanup the entire tree starting from the deepest nodes and // working upwards. // This duplicates work of Algorithm::apply, but we want to have an // independent cleanup unit which does not rely on things we may // want to change in Algorithm::apply in the future, and we do not // want to make recursive calls into that function either. And it is // simple enough anyway. // do_subtree(tr, top, [&dispatch, &tr, &k](Ex::iterator it) { // dispatch(k, tr, it); // return it; // }); Ex::post_order_iterator it=tr.begin(); it.descend_all(); while(it!=tr.end()) { Ex::post_order_iterator next=it; ++next; Ex::iterator tmp=it; dispatch(k, tr, tmp); it=next; } } }