-- Copyright 2009 Mikael Vejdemo Johansson <mik@stanford.edu>-- Released under a BSD license-- | The module 'OperadGB' carries the implementations of the Buchberger algorithm and most utility functions -- related to this.moduleMath.Operad.OperadGBwhereimportPreludehiding(mapM,sequence)importData.List(sort,sortBy,findIndex,nub,(\\),permutations)importData.OrdimportData.Foldable(foldMap,Foldable)importControl.Monadhiding(mapM)importData.Maybe#if defined USE_MAPOPERADimportMath.Operad.MapOperad#elif defined USE_POLYBAGimportMath.Operad.PolyBag#elseimportMath.Operad.MapOperad#endifimportMath.Operad.OrderedTree#ifdef TRACEimportDebug.TraceimportMath.Operad.PPrint#endif-- * Fundamental data types and instances-- | The number of internal vertices of a tree.operationDegree::(Orda,Showa)=>DecoratedTreea->IntoperationDegree(DTLeaf_)=0operationDegreevertex=1+sum(mapoperationDegree(subTreesvertex))-- | A list of operation degrees occurring in the terms of the operad elementoperationDegrees::(Orda,Showa,TreeOrderingt,Numn)=>OperadElementant->[Int]operationDegreesop=foldMonomials(\(k,_)lst->lst++[(operationDegree.dt$k)])op-- | The maximal operation degree of an operadic elementmaxOperationDegree::(Orda,Showa,TreeOrderingt,Numn)=>OperadElementant->IntmaxOperationDegreeelement=ifnullopthen0elsemaximumopwhereop=operationDegreeselement-- | Check that an element of a free operad is homogenousisHomogenous::(Orda,Showa,TreeOrderingt,Numn)=>OperadElementant->BoolisHomogenousm=lettrees=getTreesm-- equalityCheck :: OrderedTree a t -> OrderedTree a t -> BoolequalityCheckst=arityDegree(dts)==arityDegree(dtt)&&operationDegree(dts)==operationDegree(dtt)inand$zipWith(equalityCheck)trees(tailtrees)-- * Free operad-- ** Operadic compositions-- | Composition in the shuffle operadshuffleCompose::(Orda,Showa)=>Int->Shuffle->DecoratedTreea->DecoratedTreea->DecoratedTreeashuffleComposeishst|not(isPermutationsh)=error"shuffleCompose: sh needs to be a permutation\n"|(nLeavess)+(nLeavest)-1/=lengthsh=error$"Permutation permutes the wrong number of things:"++showi++" "++showsh++" "++shows++" "++showt++"\n"|not(isShuffleIPQshi(nLeavest-1))=error$"Need a correct pointed shuffle permutation!\n"++showsh++" is not in Sh"++showi++"("++show(nLeavest-1)++","++show(nLeavess-i)++")\n"|otherwise=symmetricComposeishst-- | Composition in the non-symmetric operad. We compose s o_i t. nsCompose::(Orda,Showa)=>Int->DecoratedTreea->DecoratedTreea->DecoratedTreeansComposeist=ifi-1>nLeavessthenerror"Composition point too large"elseletpS=rePackLabelsslookupList=zip(leafOrders)(leafOrderpS)idx=ifisNothingnewIthenerror"Index not in tree"elsefromJustnewIwherenewI=lookupilookupListtrees=mapDTLeaf[1..nLeavess]newTrees=take(idx-1)trees++[t]++dropidxtreesiniflengthnewTrees/=nLeavessthenerror"Shouldn't happen"elsensComposeAllsnewTrees-- | Composition in the symmetric operadsymmetricCompose::(Orda,Showa)=>Int->Shuffle->DecoratedTreea->DecoratedTreea->DecoratedTreeasymmetricComposeishst=ifnot(isPermutationsh)thenerror"symmetricCompose: sh needs to be a permutation\n"elseif(nLeavess)+(nLeavest)-1/=lengthshthenerror"Permutation permutes the wrong number of things.\n"elsefmap((sh!!).(subtract1))$nsComposeist-- | Non-symmetric composition in the g(s;t1,...,tk) style. nsComposeAll::(Orda,Showa)=>DecoratedTreea->[DecoratedTreea]->DecoratedTreeansComposeAllstrees=ifnLeavess/=lengthtreesthenerror"NS: Need as many trees as leaves\n"elseiflengthtrees==0thenselselettreesArities=mapnLeavestreespackedTrees=maprePackLabelstreesoffSets=(0:)$scanl1(+)treesAritiesnewTrees=zipWith(\tn->fmap(+n)t)packedTreesoffSetsinrePackLabels$glueTrees$fmap((newTrees!!).(subtract1))$rePackLabelss-- | Verification for a shuffle used for the g(s;t1,..,tk) style composition in the shuffle operad.checkShuffleAll::Shuffle->[Int]->BoolcheckShuffleAllshblockL=letcheckOrders::Shuffle->[Int]->BoolcheckOrders[]_=TruecheckOrders_[]=TruecheckOrdersrestShrestBlock=(isSorted(take(headrestBlock)restSh))&&(lengthrestSh<=headrestBlock||(headrestSh)<(head(drop(headrestBlock)restSh)))&&checkOrders(drop(headrestBlock)restSh)(tailrestBlock)insumblockL==lengthsh&&checkOrdersshblockL-- | Sanity check for permutations. isPermutation::Shuffle->BoolisPermutationsh=and((zipWith(==)[1..])(sortsh))-- | Shuffle composition in the g(s;t1,...,tk) style. shuffleComposeAll::(Orda,Showa)=>Shuffle->DecoratedTreea->[DecoratedTreea]->DecoratedTreeashuffleComposeAllshstrees=ifnLeavess/=lengthtreesthenerror"Shuffle: Need as many trees as leaves\n"elseifsum(mapnLeavestrees)/=lengthshthenerror"Permutation permutes the wrong number of things.\n"elseifnot(isPermutationsh)thenerror"shuffleComposeAll: sh needs to be a permutation\n"elseiflengthtrees==0thenselseifnot(checkShuffleAllsh(mapnLeavestrees))thenerror"Bad shuffle"elseletnewTree=nsComposeAllstreesinfmap((sh!!).(subtract1))newTree-- | Symmetric composition in the g(s;t1,...,tk) style. symmetricComposeAll::(Orda,Showa)=>Shuffle->DecoratedTreea->[DecoratedTreea]->DecoratedTreeasymmetricComposeAllshstrees=ifnLeavess/=lengthtreesthenerror"Symm: Need as many trees as leaves\n"elseifsum(mapnLeavestrees)/=lengthshthenerror"Permutation permutes the wrong number of things.\n"elseifnot(isPermutationsh)thenerror"sh needs to be a permutation"elseiflengthtrees==0thenselseletnewTree=nsComposeAllstreesinfmap((sh!!).(subtract1))newTree-- ** Divisibility among trees-- | Data type to move the results of finding an embedding point for a subtree in a larger tree-- around. The tree is guaranteed to have exactly one corolla tagged Nothing, the subtrees on top of-- that corolla sorted by minimal covering leaf in the original setting, and the shuffle carried around-- is guaranteed to restore the original leaf labels before the search.typeEmbeddinga=DecoratedTree(Maybea)-- | Returns True if there is a subtree of @t@ isomorphic to s, respecting leaf orders. divides::(Orda,Showa)=>DecoratedTreea->DecoratedTreea->Booldividesst=not.null$findAllEmbeddingsst-- | Finds all ways to embed s into t respecting leaf orders.findAllEmbeddings::(Orda,Showa)=>DecoratedTreea->DecoratedTreea->[Embeddinga]findAllEmbeddings_(DTLeaf_)=[]findAllEmbeddingsst=letrootFind=maybeToList$findRootedEmbeddingstsubFinds=map(findAllEmbeddingss)(subTreest)reGlue(i,ems)=letglueTreetree=(DTVertex(Just$vertexTypet)(take(i-1)(maptoJustTree$subTreest)++[tree]++dropi(maptoJustTree$subTreest)))inmapglueTreeemsinrootFind++concatMapreGlue(zip[1..]subFinds)-- | Finds all ways to embed s into t, respecting leaf orders and mapping the root of s to the root of t.findRootedEmbedding::(Orda,Showa)=>DecoratedTreea->DecoratedTreea->Maybe(Embeddinga)findRootedEmbedding(DTLeaf_)t=Just(DTVertexNothing[toJustTreet])findRootedEmbedding(DTVertex__)(DTLeaf_)=NothingfindRootedEmbeddingst=doguard$vertexAritys==vertexAritytguard$vertexTypes==vertexTypetguard$equivalentOrders(mapminimalLeaf(subTreess))(mapminimalLeaf(subTreest))letmTreeFinds=zipWithfindRootedEmbedding(subTreess)(subTreest)guard$allisJustmTreeFindslettreeFinds=mapfromJustmTreeFindsguard$all(isNothing.vertexType)treeFindsguard$equivalentOrders(leafOrders)(concatMap(mapminimalLeaf.subTrees)treeFinds)return$DTVertexNothing(sortBy(comparingminimalLeaf)(concatMapsubTreestreeFinds))-- | Finds a large common divisor of two trees, such that it embeds into both trees, mapping its root -- to the roots of the trees, respectively. findRootedDecoratedGCD::(Orda,Showa)=>DecoratedTreea->DecoratedTreea->Maybe(PreDecoratedTreea(DecoratedTreea,DecoratedTreea))findRootedDecoratedGCD(DTLeafk)t=Just$DTLeaf(DTLeafk,t)findRootedDecoratedGCDs(DTLeafk)=Just$DTLeaf(s,DTLeafk)findRootedDecoratedGCDst=doguard$vertexAritys==vertexAritytguard$vertexTypes==vertexTypetletmrdGCDs=zipWithfindRootedDecoratedGCD(subTreess)(subTreest)guard$allisJustmrdGCDsletrdGCDs=mapfromJustmrdGCDsreturn$DTVertex(vertexTypes)rdGCDs-- | Finds all small common multiples of trees s and t, under the assumption that the common multiples shares-- root with both trees.findRootedLCM::(Orda,Showa)=>DecoratedTreea->DecoratedTreea->[DecoratedTreea]findRootedLCMst=filter(\tree->dividesstree&&dividesttree)$ifoperationDegrees<operationDegreetthenfindRootedLCMtselsedoletmrdGCD=findRootedDecoratedGCDstguard$isJustmrdGCDletrdGCD=fromJustmrdGCDleafDecorations=foldMap(:[])rdGCDrebuildRecipe=reverse.sortBy(comparingfst)$filter(isLeaf.fst)leafDecorationsaccumulateTreesrebuildRecipe[s]-- | Internal utility function. Reassembles a tree according to a "building recipe", and gives the orbit-- of the resulting tree under the symmetric group action back.accumulateTrees::(Orda,Showa)=>[(DecoratedTreea,DecoratedTreea)]->[DecoratedTreea]->[DecoratedTreea]accumulateTrees[]partialTrees=partialTreesaccumulateTrees((aLeaf,tree):rs)partialTrees=ifnot$isLeafaLeafthenerror"Should have a leaf"elseletnewTrees=dopartialTree<-partialTreesletidx=minimalLeafaLeafnewTree=rePackLabelstreepackedPartialTree=rePackLabelspartialTreelookupList=zip(leafOrderpartialTree)(leafOrderpackedPartialTree)i=fromJust$lookupidxlookupListreturn$nsComposeipackedPartialTreenewTreeindot<-accumulateTreesrsnewTreesp<-permutations[1..nLeavest]letreturnTree=relabelLeavestpguard$planarTreereturnTreereturn$returnTree-- | Checks a tree for planarity.planarTree::(Orda,Showa)=>DecoratedTreea->BoolplanarTree(DTLeaf_)=TrueplanarTree(DTVertex_subs)=allplanarTreesubs&&isSorted(mapminimalLeafsubs)-- | Finds all small common multiples of s and t such that t glues into s from above, bounded in total operation degree.findSmallBoundedLCM::(Orda,Showa)=>Int->DecoratedTreea->DecoratedTreea->[DecoratedTreea]findSmallBoundedLCM_(DTLeaf_)_=[]findSmallBoundedLCM__(DTLeaf_)=[]findSmallBoundedLCM0__=[]findSmallBoundedLCMnst=nub$filter(dividess)$filter(isJust.findRootedEmbeddingt)$do-- find rLCMs of s and t.-- find LCMs of all subtrees of s with t-- for those, reglue the rest of tletrootedLCMs=if(operationDegrees)>n||(operationDegreet)>nthen[]elsefindRootedLCMstchildLCMs=map(findSmallBoundedLCM(n-1)s)(subTreest)reGlue(i,ems)=ifi>length(subTreest)thenerror"Too high composition point, findSmallLCM:reGlue"elselettemplate=rePackLabels$DTVertex(vertexTypet)(take(i-1)(subTreest)++[leaf(minimalLeaf(subTreest!!(i-1)))]++dropi(subTreest))inconcatMap(\emt->accumulateTrees[(leafi,emt)][template])emszippedChildLCMs=zip[1..]childLCMsfilter((<=n).operationDegree)rootedLCMs++(concatMapreGluezippedChildLCMs)-- | Finds all small common multiples of s and t.findAllLCM::(Orda,Showa)=>DecoratedTreea->DecoratedTreea->[DecoratedTreea]findAllLCMst=(findSmallBoundedLCMmaxBoundst)++(findSmallBoundedLCMmaxBoundts)-- | Finds all small common multiples of s and t, bounded in total operation degree. findAllBoundedLCM::(Orda,Showa)=>Int->DecoratedTreea->DecoratedTreea->[DecoratedTreea]findAllBoundedLCMnst=(findSmallBoundedLCMnst)++(findSmallBoundedLCMnts)-- | Relabels a tree in the right order, but with entries from [1..]rePackLabels::(Orda,Showa,Ordb)=>PreDecoratedTreeab->DecoratedTreearePackLabelstree=fmap(fromJust.(fliplookup(zip(sort(foldMap(:[])tree))[1..])))tree-- | Removes vertex type encapsulations.fromJustTree::(Orda,Showa)=>DecoratedTree(Maybea)->Maybe(DecoratedTreea)fromJustTree(DTLeafk)=Just(DTLeafk)fromJustTree(DTVertexNothing_)=NothingfromJustTree(DTVertex(Justl)sts)=letnewsts=mapfromJustTreestsinifallisJustnewststhenJust$DTVertexl(mapfromJustnewsts)elseNothing-- | Adds vertex type encapsulations. toJustTree::(Orda,Showa)=>DecoratedTreea->DecoratedTree(Maybea)toJustTree(DTLeafk)=DTLeafktoJustTree(DTVertexasts)=DTVertex(Justa)(maptoJustTreests)-- replace the following function by one that zips lists, sorts them once, and then unsplits them,-- verifying both lists to be sorted afterwards?-- | Verifies that two integer sequences correspond to the same total ordering of the entries.equivalentOrders::[Int]->[Int]->BoolequivalentOrderso1o2=iflengtho1/=lengtho2thenFalseelseand$zipWith(==)(zipWithcompareo1(tailo1))(zipWithcompareo2(tailo2))-- | Returns True if any of the vertices in the given tree has been tagged.subTreeHasNothing::(Orda,Showa)=>DecoratedTree(Maybea)->BoolsubTreeHasNothing(DTLeaf_)=FalsesubTreeHasNothing(DTVertexNothing_)=TruesubTreeHasNothing(DTVertex(Just_)sts)=anysubTreeHasNothingsts-- | The function that mimics resubstitution of a new tree into the hole left by finding embedding,-- called m_\alpha,\beta in Dotsenko-Khoroshkin. This version only attempts to resubstitute the tree-- at the root, bailing out if not possible.reconstructNode::(Orda,Showa)=>DecoratedTreea->Embeddinga->Maybe(DecoratedTreea)reconstructNodesubsuper=ifisJust(vertexTypesuper)thenNothingelseif(nLeavessub)/=(vertexAritysuper)thenNothingelseletnewSubTrees=mapfromJustTree(subTreessuper)inifanyisNothingnewSubTreesthenNothingelseletnewTrees=mapfromJustnewSubTreesleafs=concatMapleafOrdernewTreesnewTree=nsComposeAllsubnewTreesinJust$fmap((leafs!!).(subtract1))newTree-- | The function that mimics resubstitution of a new tree into the hole left by finding embedding,-- called m_\alpha,\beta in Dotsenko-Khoroshkin. This version recurses down in the tree in order-- to find exactly one hole, and substitute the tree sub into it.reconstructTree::(Orda,Showa)=>DecoratedTreea->Embeddinga->Maybe(DecoratedTreea)reconstructTreesubsuper=ifisLeafsuperthenNothingelseifisNothing(vertexTypesuper)thenreconstructNodesubsuperelseif(1/=).sum.mapfromEnum$mapsubTreeHasNothing(subTreessuper)thenNothingelseletfromMaybeByfst=ifisNothingtthenfselsetsubtreesSuper=subTreessuperreconstructions=map(reconstructTreesub)subtreesSuperresults=zipWith(fromMaybeByfromJustTree)subtreesSuperreconstructionsinifanyisNothingresultsthenNothingelseJust$DTVertex(fromJust$vertexTypesuper)(mapfromJustresults)-- ** Groebner basis methods-- | Applies the reconstruction map represented by em to all trees in the operad element op. Any operad element that -- fails the reconstruction (by having the wrong total arity, for instance) will be silently dropped. We recommend-- you apply this function only to homogenous operad elements, but will not make that check.applyReconstruction::(Orda,Showa,TreeOrderingt,Numn)=>Embeddinga->OperadElementant->OperadElementantapplyReconstructionemm=letreconstructor(ordT,n)=donewDT<-reconstructTree(dtordT)emreturn$(otnewDT,n)inoe$mapMaybereconstructor(toListm)-- | Finds all S polynomials for a given list of operad elements. findAllSPolynomials::(Orda,Showa,TreeOrderingt,Fractionaln)=>[OperadElementant]->[OperadElementant]->[OperadElementant]findAllSPolynomials=findInitialSPolynomialsmaxBound-- | Finds all S polynomials for which the operationdegree stays bounded.findInitialSPolynomials::(Orda,Showa,TreeOrderingt,Fractionaln)=>Int->[OperadElementant]->[OperadElementant]->[OperadElementant]findInitialSPolynomialsnoldGbnewGb=nub.map(\o->(1/leadingCoefficiento).*.o).filter(not.isZero)$dog1<-oldGb++newGbg2<-newGbletlmg1=leadingMonomialg1lmg2=leadingMonomialg2cf12=(leadingCoefficientg1)/(leadingCoefficientg2)gamma<-nub$findAllBoundedLCMnlmg1lmg2#ifdef TRACEtrace("Found LCM: \n\t"++pplmg1++", \n\t"++pplmg2++": \n\t"++ppgamma++"\n")(return())#endifmg1<-findAllEmbeddingslmg1gammamg2<-findAllEmbeddingslmg2gammareturn$(applyReconstructionmg1g1)-(cf12.*.(applyReconstructionmg2g2))-- | Reduce g with respect to f and the embedding em: lt f -> lt g.reduceOE::(Orda,Showa,TreeOrderingt,Fractionaln)=>Embeddinga->OperadElementant->OperadElementant->OperadElementantreduceOEemfg=ifnot(divides(leadingMonomialf)(leadingMonomialg))thengelseletcgf=(leadingCoefficientg)/(leadingCoefficientf)ret=g-(cgf.*.(applyReconstructionemf))inifisZeroretthenretelse(1/leadingCoefficientret).*.retreduceCompletely::(Orda,Showa,TreeOrderingt,Fractionaln)=>OperadElementant->[OperadElementant]->OperadElementantreduceCompletelyop[]=opreduceCompletelyopgb=ifisZeroopthenopelseletdivisorIdx=findIndex(flipdivides(leadingMonomialop))(mapleadingMonomialgb)inifisNothingdivisorIdxthenopelseletg1=gb!!(fromJustdivisorIdx)em=head$findAllEmbeddings(leadingMonomialg1)(leadingMonomialop)o1=reduceOEemg1opinreduceCompletelyo1gb-- | Perform one iteration of the Buchberger algorithm: generate all S-polynomials. Reduce all S-polynomials.-- Return anything that survived the reduction.stepOperadicBuchberger::(Orda,Showa,TreeOrderingt,Fractionaln)=>[OperadElementant]->[OperadElementant]->[OperadElementant]stepOperadicBuchbergeroldGbnewGb=stepInitialOperadicBuchbergermaxBoundoldGbnewGb-- | Perform one iteration of the Buchberger algorithm: generate all S-polynomials. Reduce all S-polynomials.-- Return anything that survived the reduction. Keep the occurring operation degrees bounded. stepInitialOperadicBuchberger::(Orda,Showa,TreeOrderingt,Fractionaln)=>Int->[OperadElementant]->[OperadElementant]->[OperadElementant]stepInitialOperadicBuchbergermaxDoldGbnewGb=nub$filter(not.isZero)$dospol<-findInitialSPolynomialsmaxDoldGbnewGbguard$maxOperationDegreespol<=maxDletred=#ifdef TRACEtrace("Reducing S-polynomial: "++ppspol++"\n")$#endifreduceCompletelyspol(oldGb++newGb)guard$not.isZero$redreturnred-- | Perform the entire Buchberger algorithm for a given list of generators. Iteratively run the single iteration-- from 'stepOperadicBuchberger' until no new elements are generated.---- DO NOTE: This is entirely possible to get stuck in an infinite loop. It is not difficult to write down generators-- such that the resulting Groebner basis is infinite. No checking is performed to catch this kind of condition.operadicBuchberger::(Orda,Showa,TreeOrderingt,Fractionaln)=>[OperadElementant]->[OperadElementant]operadicBuchbergergb=nub$initialOperadicBuchbergermaxBoundgb-- | Perform the entire Buchberger algorithm for a given list of generators. Iteratively run the single iteration-- from 'stepOperadicBuchberger' until no new elements are generated. While doing this, maintain an upper bound-- on the operation degree of any elements occurring.--initialOperadicBuchberger::(Orda,Showa,TreeOrderingt,Fractionaln)=>Int->[OperadElementant]->[OperadElementant]initialOperadicBuchbergermaxODgb=letoperadicBuchbergerAccoldgb[]=oldgboperadicBuchbergerAccoldgbnew=ifminimum(mapmaxOperationDegreenew)>maxODthenoldgbelseletgbn=#ifdef TRACEtrace("Stepping through\n")$#endifstepInitialOperadicBuchbergermaxODoldgbnewgbo=reduceBasis$oldgb++newgbc=(reduceBasis(gbn++gbo))\\gboinoperadicBuchbergerAccgbogbcinnub$operadicBuchbergerAcc[]gb-- | Reduces a list of elements with respect to all other elements occurring in that same list.reduceBasis::(Orda,Showa,TreeOrderingt,Fractionaln)=>[OperadElementant]->[OperadElementant]reduceBasisgb=letreduceAccngb[]=ngbreduceAccngb(g:gs)=letng=reduceCompletelygngbngb'=ifisZerongthenngbelseng:ngbinreduceAccngb'gsinreduceAcc[](reverse.sortBy(comparingleadingMonomial)$gb)-- ** Low degree bases-- | All trees composed from the given generators of operation degree n.allTrees::(Orda,Showa)=>[DecoratedTreea]->Int->[DecoratedTreea]allTrees_0=[]allTreesgens1=gensallTreesgensk=nub$dogen<-genstree<-allTreesgens(k-1)letdegC=nLeavesgendegT=nLeavestreei<-[1..degT]shuffle<-allShufflesi(degC-1)(degT-i)return$shuffleComposeishuffletreegen-- | Generate basis trees for a given Groebner basis up through degree 'maxDegree'. 'divisors' is expected-- to contain the leading monomials in the Groebner basis.basisElements::(Orda,Showa)=>[DecoratedTreea]->[DecoratedTreea]->Int->[DecoratedTreea]basisElementsgeneratorsdivisorsmaxDegree=nub$ifmaxDegree<=0then[]elseifmaxDegree==1thengenerators-- else if null divisors then allTrees generators maxDegreeelsedob<-basisElements'generatorsdivisors(maxDegree-1)gen<-generatorsletdegC=nLeavesgendegT=nLeavesbi<-[1..degT]shuffle<-allShufflesi(degC-1)(degT-i)letnewB=shuffleComposeishufflebgenguard$not$any(flipdividesnewB)divisorsreturnnewBbasisElements'::(Orda,Showa)=>[DecoratedTreea]->[DecoratedTreea]->Int->[DecoratedTreea]basisElements'generatorsdivisorsmaxDegree=ifnulldivisorsthenallTreesgeneratorsmaxDegreeelsedob<-allTreesgeneratorsmaxDegreeguard$not$any(flipdividesb)divisorsreturnb-- | Change the monomial order used for a specific tree. Use this in conjunction with mapMonomials-- in order to change monomial order for an entire operad element. changeOrder::(Orda,Showa,TreeOrderings,TreeOrderingt)=>t->OrderedTreeas->OrderedTreeatchangeOrdero'(OTt_)=OTto'