1*e1fe3e4aSElliott Hughes"""Generic tools for working with trees.""" 2*e1fe3e4aSElliott Hughes 3*e1fe3e4aSElliott Hughesfrom math import ceil, log 4*e1fe3e4aSElliott Hughes 5*e1fe3e4aSElliott Hughes 6*e1fe3e4aSElliott Hughesdef build_n_ary_tree(leaves, n): 7*e1fe3e4aSElliott Hughes """Build N-ary tree from sequence of leaf nodes. 8*e1fe3e4aSElliott Hughes 9*e1fe3e4aSElliott Hughes Return a list of lists where each non-leaf node is a list containing 10*e1fe3e4aSElliott Hughes max n nodes. 11*e1fe3e4aSElliott Hughes """ 12*e1fe3e4aSElliott Hughes if not leaves: 13*e1fe3e4aSElliott Hughes return [] 14*e1fe3e4aSElliott Hughes 15*e1fe3e4aSElliott Hughes assert n > 1 16*e1fe3e4aSElliott Hughes 17*e1fe3e4aSElliott Hughes depth = ceil(log(len(leaves), n)) 18*e1fe3e4aSElliott Hughes 19*e1fe3e4aSElliott Hughes if depth <= 1: 20*e1fe3e4aSElliott Hughes return list(leaves) 21*e1fe3e4aSElliott Hughes 22*e1fe3e4aSElliott Hughes # Fully populate complete subtrees of root until we have enough leaves left 23*e1fe3e4aSElliott Hughes root = [] 24*e1fe3e4aSElliott Hughes unassigned = None 25*e1fe3e4aSElliott Hughes full_step = n ** (depth - 1) 26*e1fe3e4aSElliott Hughes for i in range(0, len(leaves), full_step): 27*e1fe3e4aSElliott Hughes subtree = leaves[i : i + full_step] 28*e1fe3e4aSElliott Hughes if len(subtree) < full_step: 29*e1fe3e4aSElliott Hughes unassigned = subtree 30*e1fe3e4aSElliott Hughes break 31*e1fe3e4aSElliott Hughes while len(subtree) > n: 32*e1fe3e4aSElliott Hughes subtree = [subtree[k : k + n] for k in range(0, len(subtree), n)] 33*e1fe3e4aSElliott Hughes root.append(subtree) 34*e1fe3e4aSElliott Hughes 35*e1fe3e4aSElliott Hughes if unassigned: 36*e1fe3e4aSElliott Hughes # Recurse to fill the last subtree, which is the only partially populated one 37*e1fe3e4aSElliott Hughes subtree = build_n_ary_tree(unassigned, n) 38*e1fe3e4aSElliott Hughes if len(subtree) <= n - len(root): 39*e1fe3e4aSElliott Hughes # replace last subtree with its children if they can still fit 40*e1fe3e4aSElliott Hughes root.extend(subtree) 41*e1fe3e4aSElliott Hughes else: 42*e1fe3e4aSElliott Hughes root.append(subtree) 43*e1fe3e4aSElliott Hughes assert len(root) <= n 44*e1fe3e4aSElliott Hughes 45*e1fe3e4aSElliott Hughes return root 46