Simpler Editing of Graph-Based Segmentation Hierarchies using Zipping Algorithms S upplementary M aterial

This document contains: (i) a more detailed explanation of hierarchical selections, (ii) a detailed qualitative example in which we compare non-sibling node merging to the equivalent sequence of manual split and merge operations, (iii) pseudo-code for our algorithms to aid those who wish to implement them, (iv) a number of important proofs about our algorithms

In order for users to interact with graph hierarchies, they must be able to specify parts of them with which to work.For example, to perform a multi-node unzip (see Section 4.1.2 of the main paper), they must be able to select a set of nodes to unzip.
Whilst it is possible to represent a selection of nodes in a hierarchy as a simple set, this models a slightly unnatural concept of selection that allows parent and child nodes in the hierarchy to be individually selected / deselected.A more natural, hierarchical concept of selection (Golodetz (2011); Golodetz et al. (2009)) can be derived from the notion that selecting / deselecting a node in the hierarchy should implicitly select / deselect all of its descendants.We refer to the data structure that models this concept of selection as a hierarchical selection over a graph hierarchy (see Figure 6 of the main paper): internally, it represents a selection of nodes in the base graph of the hierarchy using a set of stored nodes that is as small as possible (with a preference for nodes that are higher up in the hierarchy when there is a choice).
The three most important operations that hierarchical selections support are (i) adding a node, (ii) removing a node, and (iii) checking whether a node is selected.We briefly describe how node addition and removal work in the following subsections.To check whether a node is selected, we simply test whether it, or any of its ancestors in the graph hierarchy, are contained in the set of stored nodes.

Adding a node
There are three cases to consider when adding a node: (i) The node is already selected (either it or one of its ancestors is contained in the set of stored nodes).
(ii) Neither the node itself nor any of its descendants is selected.
(iii) The node itself is not selected, but one or more of its descendants is selected.
In case (i), there is clearly nothing to do.Cases (ii) and (iii) require more work.In case (iii), all descendants of the node must first be removed from the set of stored nodes.Then, in both cases (ii) and (iii), the node to be selected must be added to the set of stored nodes.Finally, the selection must be consolidated by replacing any node whose children are all in the set of stored nodes with the node itself, until this is no longer possible.In practice, only ancestors of the added node need to be considered during consolidation: all other nodes in the representation will be unaffected by the addition of the node.An example of case (iii), which is the most general case, is shown in Figure 1.Additional implementation details, including pseudo-code, can be found in Golodetz (2011).

Removing a node
There are four cases to consider when removing a node: (i) Neither the node itself nor any of its ancestors or descendants is contained in the set of stored nodes.
(ii) The node itself is contained in the set of stored nodes.
(iii) The node itself is not contained in the set of stored nodes, but one or more of its descendants is.
(iv) The node itself is not contained in the set of stored nodes, but one of its ancestors is.
In case (i), there is clearly nothing to do.Case (ii) simply involves removing the node from the set of stored nodes.In case (iii), it suffices to enumerate all the descendants of the node that are in the set of stored nodes and remove them.Case (iv), in which one of the node's ancestors is in the set of stored nodes, is more interesting.In this case, we must find the trail of nodes in the hierarchy that leads down from the ancestor in question to the node we want to remove (not including the node itself).We then replace all the nodes on this trail (from the ancestor downwards) in the set of stored nodes with their children, until the node we want to remove is itself in the set of stored nodes, at which point we can simply remove it.An example of this process is shown in Figure 2. Further details and pseudo-code can be found in Golodetz (2011).

Non-Sibling Node Merging vs. Manual Split and Merge
The non-sibling node merging operation we describe in Section 5.1 of the main paper is designed to be both conceptually simple to understand and intuitive to use in practice.However, some non-sibling merges (particularly those that involve merging nodes whose lowest common ancestor is multiple layers up in the hierarchy) can involve significant structural changes behind the scenes.By hiding this complexity, non-sibling node merging significantly reduces the interaction burden on the user for such merges.In this section, we demonstrate this using a merge that is quite difficult to perform manually, but trivial to perform using our algorithm.Figure 3 shows an example in which the user is trying to merge four nodes (shown in (a)) in layer 3 of a graph hierarchy for an abdominal CT scan (the four nodes together correspond to the right kidney of the patient in question).However, because the nodes do not share a common parent or grandparent in layers 4 and 5 of the hierarchy (as shown in (b) and (c)), they cannot be merged using straightforward sibling node merging; instead, we must perform a non-sibling node merge by unzipping them up to just below their common ancestor (in this case the top of the hierarchy) and zipping the resulting node chains back down the hierarchy to complete the merge.Performed manually, this is a laborious process: since there are two parents and two grandparents of the nodes, four separate split operations must be performed, and three sibling node merge operations must then be performed to complete the process.Moreover, whilst sibling node merges are easy for the user to specify, splits are not: for each split, the user must specify the components into which to split the node, which can consume significant time.Using our non-sibling node merging algorithm, exactly the same result can be achieved in a single step: the user simply needs to select the nodes (as in (a)) and request that they be merged; all of the tedious book-keeping can be performed automatically behind the scenes.

Pseudo-Code
Listings 1-5 provide commented pseudo-code for the algorithms we describe in the main paper, to assist those who wish to reimplement them.A reference for the pseudo-code language we use can be found in Appendix F of Golodetz (2011).The data structures we use (such as Vector, Set and Map) correspond to those that can be found in the standard libraries of many popular languages, although they largely mirror std::vector, std::set and std::map from C++ (Stroustrup (2013)).
In a practical implementation, the efficiency of our algorithms depends on the choice made about how to represent the higher-level edges in the hierarchy.For example, at various points, we need to compute the connected components of a set of nodes at a particular depth in the hierarchy, a task that can be achieved much more efficiently if the edges at that depth are readily available than if they have to be continually recomputed from the base graph (this difference in efficiency is particularly 2  3 (y) Figure 3: An example showing how an invocation of our non-sibling node merging algorithm can replace numerous manual steps, reducing the interaction burden on the user.The number on each image denotes its hierarchy layer.In (a), the user selects a set of nodes to be merged (in layer 3 of a hierarchy).The parents and grandparents of the nodes (in layers 4 and 5) are shown in (b) and (c) respectively: note that the nodes do not have a common parent or grandparent, so their parents and grandparents must be split before they themselves can be merged.Subfigures (d)-(g) show the first parent node being split: (d) the node to be split is selected; (e) the split is requested; (f) the components into which to split the node are specified; (g) the split is finalised.Subfigures (h)-(s) show the same process for the first grandparent, the second parent and the second grandparent (there are four subfigures for each, denoted with green, blue and yellow borders respectively).Finally, straightforward sibling node merging is used to zip the nodes together again back down the hierarchy: (t) and (u) show the grandparents being merged, (v) and (w) show the parents being merged, and (x) and (y) show the original nodes being merged.To achieve this result manually, we have had to perform the four split operations in (d)-(g), (h)-(k), (l)-(o) and (p)-(s) (specifying the split components in each case) and three sibling merge operations.The same result can be achieved in a single step using our non-sibling node merging algorithm.
relevant in a deep hierarchy).In millipede, we therefore explicitly store the edges at each depth, and maintain them when splitting or merging nodes.This needs to be borne in mind when we analyse the complexity of our algorithms in Section 5.
Furthermore, we note that for practical reasons, the implementations of our algorithms differ in a few places from the mathematical definitions presented in the main text.We mention these differences on a per-algorithm basis here for the avoidance of confusion.

Single-Node Unzipping
• The splitting of nodes is more general in the practical implementation.In the main text, we defined a splitPar operation that specifies the nodes that would result from splitting a parent node around some of its children.In practice, we define a more general hierarchy-mutating operation called split node that splits the specified parent node into pieces corresponding to a specified set of connected components.For single-node unzipping, these connected components will be the ones specified in the definition of splitPar, but split node can actually be used to split a node into pieces corresponding to an arbitrary set of connected components.This is practically useful in that it allows us to provide users with a general splitting operation in the interface.
• The chains are not stored as a set in practice.We observe that both of the higher-level algorithms (non-sibling node merging and parent switching) use single-node unzipping and then need to access the chain in the resulting chain set corresponding to the parent of the node that was being unzipped.To make this lookup efficient, we therefore implement the chain set as a vector (i.e. an array) of chains, and arrange for the first chain in the array to be the one that corresponds to the parent of the node that was being unzipped.To do this, we add a singleton chain containing the node to be unzipped to the chain set at the start of the algorithm.The final chain should not contain the actual node being unzipped (only the nodes above it), so we remove it from the end of the chain again before returning.The practical effect of this optimisation is to make the lookup of the relevant chain constant time.

Multi-Node Unzipping
• The grouping of current nodes by parent is handled straightforwardly in splitPars in the mathematical definition, whereas here we have to group them manually.
• Again, the chains are not stored as a set, since we need a convenient way to look up individual chains.Unlike for single-node unzipping, here there is no obvious chain to prioritise, so we instead store the chains as a map, keyed on the deepest node in each chain.

Non-Sibling Node Merging
• Unlike in the mathematical definition, here we avoid trying to merge the nodes in a set K i ∈ K of size 1, to save computation.

Our Editing Algorithms Preserve Region Contiguity
As explained in Section 3 of the main paper, an important property of each hierarchy that we consider in this paper is that the regions of all of the nodes in the hierarchy are connected in the hierarchy's base graph.For convenience, we will refer to this property as region contiguity.In this section, we prove that this property is preserved by all of our editing algorithms.First of all, recall the closed form definition of multi-node unzipping from the main paper: Lemma 1. Multi-node unzipping preserves the region contiguity of the hierarchy.
Proof.The effect of a multi-node unzip is to remove some of the ancestors of the nodes in N, and to replace them with the results of splitting them around their respective descendants in N.There are thus precisely two types of changes being made to the hierarchy: some existing nodes are being removed, and some new nodes resulting from calls to splitAncs are being added.Removing existing nodes from the hierarchy clearly preserves region contiguity, since the remaining nodes are a subset of those that were in the hierarchy before (whose regions were all connected).Moreover, each new node added comes from a call to splitAncs, and therefore indirectly from a call to splitAnc.But the regions of these nodes are connected by definition, since splitAnc yields nodes of the form (d, R ), where R is always a connected component in the hierarchy's base graph.Hence multi-node unzipping must preserve the region contiguity of the hierarchy.
Corollary 2. Single-node unzipping preserves the region contiguity of the hierarchy.
Proof.As can be seen from their definitions, multi-node unzipping strictly generalises single-node unzipping.In particular, unzipping a single node with single-node unzipping is equivalent to unzipping a singleton set containing that node with multi-node unzipping.As a result, since multi-node unzipping preserves region contiguity, so does single-node unzipping.
Now recall the definition of chain zipping from the main paper: and defined(π Lemma 3. Chain zipping preserves the region contiguity of the hierarchy. Proof.The effect of a chain zipping operation is to remove the nodes in the chains being zipped from the hierarchy, and replace them with the results of merging the nodes at each depth in the chains together.As explained above, removing nodes from the hierarchy clearly preserves region contiguity.Moreover, all the nodes that are being added in this case result from merging together nodes that we ensure are connected in the definition of zipPre (note the last conjunct).As a result, we only ever add new nodes whose regions are connected, and hence chain zipping as a whole must preserve the region contiguity of the hierarchy.
Theorem 4. Since both our unzipping and zipping algorithms preserve the region contiguity of the hierarchy, any algorithm that performs a finite sequence of such unzipping and zipping operations also preserves it.
Proof.Trivial (by induction on the length of the sequence).
Corollary 5.Both non-sibling node merging and parent switching preserve the region contiguity of the hierarchy.
Proof.Both algorithms perform finite sequences of unzipping and zipping operations, and so preserve the region contiguity of the hierarchy by Theorem 4.

Chain Zipping Reverts Single-Node Unzipping
In addition to proving that our zipping algorithms preserve important properties of the hierarchies on which they operate, it is also helpful to show how they relate to each other.In particular, the names of the operations imply that by applying an appropriate zipping operation, we ought be able to revert the effects of a previous unzip.In this section, we show that this is indeed the case, by demonstrating that zipping together the chains resulting from a single-node unzip has the effect of reverting that operation.Proof.From the definition of chains d , the nodes added are: In this, the penultimate step follows from the observation that for every Proof.If C is the set of chains resulting from the unzip, then: By recursive application of Lemma 7, the nodes contained in chains d min (and thus C) are precisely: Since each splitAnc H (d, {n}) in this only yields nodes at depth d, the result follows straightforwardly.
Having shown that single-node unzipping yields chains of this form, it only remains to show that zipping these chains back together gives us back the original hierarchy: Lemma 9. Merging the results of splitting an ancestor of a node n about n yields a set containing that ancestor.Formally: Proof.Looking at the definition of splitAnc, we argue that: Theorem 10.Zipping together the chains resulting from a single-node unzip reverts that unzip.

Our Higher-Level Algorithms are Minimally Destructive
In order to make hierarchy editing more predictable for users, it is helpful to crystallise our understanding of which parts of a hierarchy can potentially be changed by the algorithms we propose, and which parts will necessarily stay the same.In this section, we examine in particular our non-sibling node merging and parent switching algorithms, and show that despite being non-local hierarchy operations, their effects are nonetheless confined to well-defined parts of the hierarchy.As a by-product of this analysis, we also show that our algorithms are minimally destructive: every hierarchy node that is modified by either a non-sibling node merge or a parent switch would need to be modified by any operations performing the same tasks.

Parent Switching
Consider a parent switch operation on a hierarchy H that switches the parent of a node n to a new parent p.As per the main paper, this will first unzip n to a depth of d min = D(ψ + H ({n, p})) + 1, just below the lowest common ancestor of n and p in H, and then zip the resulting chain leading down to the parent of n to the chain leading down to p to complete the switch.The set of nodes N m in H that will be modified by this operation can be specified as: For example, the nodes that would be modified by an example parent switching operation are shown in Figure 4(a), in which the parent of the green leaf is being switched to the node circled in yellow, nodes in N u are shown in red and nodes in N z are shown in orange.To show that parent switching is minimally destructive, first observe that making n a child of p in a modified hierarchy H necessarily involves ensuring that: 1.The region of p and every other ancestor of n in H contains the region of n.
2. No node in H other than n or one of its ancestors has a region that contains the region of n.
To achieve this, we need to at the very least: 1. Remove R(n) from the region of any current ancestor of n in H that will still exist after the switch and no longer be an ancestor of n.The set of such nodes is precisely N u , the set consisting of every ancestor of n below the lowest common ancestor of n and p in H whose region strictly contains that of n.Every such node will still exist after the switch (since it currently represents more than just R(n)), but will no longer be an ancestor of n, and must therefore be modified by any operation effecting the switch.We note that existing ancestors of n at or above the lowest common ancestor of n and p are excluded from N u , since they will still be ancestors of n after the switch.
2. Add R(n) to the region of every node that will be an ancestor of n after the parent switch and was not an ancestor before.The set of such nodes is precisely N z , the set consisting of p and all of its ancestors below the lowest common ancestor of n and p in H, since the lowest common ancestor and all ancestors of p above it are already ancestors of n in H. Thus all nodes in N z must inevitably be modified by any operation effecting the switch.
Since our parent switching algorithm therefore only modifies hierarchy nodes that must be modified by any algorithm performing the same task, it is minimally destructive as claimed.

Non-Sibling Node Merging
A similar, though somewhat more complicated, argument can be made for non-sibling node merging.Consider a nonsibling merge of a set of nodes N in a hierarchy H.As per the main paper, the nodes in N will first be divided into their connected components K = {K 1 , . . ., K k }.Then, the nodes in each component K i will be unzipped to a depth of d i min = D(a i ) + 1, just below their lowest common ancestor a i = ψ + H (K i ).Finally, the relevant chains for each component will be zipped together to complete the merge.The set of nodes N m in H that will be modified by this operation can be specified as follows: For example, the nodes that would be modified by an example non-sibling node merging operation are shown in Figure 4 To show that non-sibling node merging is minimally destructive, first observe that to make a new hierarchy H in which the nodes in each K i are merged, any operation will necessarily need to create in H , for each K i , a new node m i at depth D(N) whose region is R(K i ).Moreover, due to the nature of parentchild relationships in a segmentation hierarchy, it will also need to ensure that the region of each ancestor For depths d < d i min , i.e. depths at and above the depth of the lowest common ancestor a i of the nodes in K i , nothing needs to change: by definition, R(a i ) ⊇ R(n) for every n ∈ K i , and the region of every ancestor of a i contains R(a i ).However, for each depth d ≥ d i min , i.e. each depth below that of a i , any operation will necessarily have to create a new node whose region contains R(K i ), since by the definition of the lowest common ancestor of K i , such nodes cannot exist in H.To do this, it will necessarily have to modify the nodes specified in N i m , the (non-trivial) ancestors of the nodes in K i at depths below that of a i : it cannot avoid this, since otherwise there would be an overlap between the regions of the new nodes it is creating and the regions of the existing nodes at each depth.
Extending this argument over all of the components in K, any operation that wants to perform this non-sibling node merge would therefore have to modify at least the nodes in N m .Since our algorithm modifies exactly the nodes in N m , it is minimally destructive as claimed.

Complexity Analysis
We first analyse the computational complexity of our algorithms in terms of the numbers of atomic split and merge operations involved in each case.By denoting the cost of an arbitrary parent-splitting operation as C s and the cost of an arbitrary sibling node merge as C m , this gives us a simple measure of complexity that is agnostic to the particular choice we make regarding the storage of higher-level edges in the hierarchy.However, as mentioned in Section 3, the storage or otherwise of higherlevel edges can make a significant difference to the efficiency of our algorithms in practice.We thus also analyse the complexity of individual splits and merges, on the assumption that we maintain the set of higher-level edges at each depth, as is done in millipede.

Main Algorithms
Single-Node Unzipping.Consider a single-node unzip that unzips a node n to depth d min .Referring back to the closed-form definition in the main paper, this effects a split of each ancestor of n at depths d ∈ [d min , D(n)).Its worst-case complexity is thus: Multi-Node Unzipping.Consider a multi-node unzip that unzips nodes in N to depth d min .In the worst case, all of these nodes will be at the same depth, namely D max (N), and each node in N will need to be unzipped individually up to depth d min .The worst-case complexity is thus: Chain Zipping.Consider a chain zip that zips together the nodes in a set of chains C. In the worst case, this will involve merging nodes at every depth from D min (N C ), the smallest depth of any node in one of the chains, to D max (N C ), the greatest depth of any node in one of the chains.The worst-case complexity is thus: Non-Sibling Node Merging.Consider a non-sibling node merge of a set of nodes N (all of which are at a common depth D(N)).As per the definition, this first divides the nodes in N into their connected components, and then unzips the nodes in each component up to just below their lowest common ancestor.In the worst case, the total cost of these unzips is as great as possible, which happens when the lowest common ancestors for all the components are at some common depth, which we will call d min .The total cost of the unzips involved in the nonsibling node merge is thus the cost of unzipping every node in N to depth d min , i.e.

Auxiliary Operations
Parent Splitting.Consider using the splitPar operation defined in the main paper to split a parent node p in a hierarchy H around a node set N containing some of its children.This involves first computing both the connected components of N, and the connected components of N = Π −1 H (p) \ N, i.e. the remaining children of p. Formally, we define the overall set of connected components into which we want to split p as where nccs computes the connected components of a set of nodes, as in the main paper.Assuming that we maintain a graph for each depth d in H, the components in K can be computed in linear time using depth-first search.More specifically, let E[X, ∩] be the set of edges in E that have both endpoints in X.Then the cost of computing K is Once the components have been computed, p must be removed from V D(p) H and replaced with a new node corresponding to each component in K. Assuming V D(p) H is represented as a hash set, the expected complexity of this is O(|K|).In the worst case, where every node in N and N yields a component, We must also remove all of the edges connected to p from E D(p) H , and replace them with the results of propagating all edges connected to any child of p up to depth D(p).Let E[X, ∪] be the set of edges in E that have at least one endpoint in X.Then the set of edges we must remove from H [{p}, ∪], and the set of edges that must be added is in which π H (n) denotes the parent of n in the modified hierarchy.Assuming E D(p) H is represented as a hash set, the expected overall complexity of the edge updates is thus Propagating the edges upwards is the dominant cost of the whole operation, so this is also the expected complexity of parent splitting as a whole.
Sibling Node Merging.Consider merging a set of sibling nodes N in a hierarchy H.To do this, we must remove all the nodes in N from V D(N) H and replace them with a single new node, m, representing the result of the merge.Assuming V D(N) H is represented as a hash set, the expected complexity of this is O(|N|).
We must also remove all of the edges in E D(N) H [N, ∩], i.e. the edges between nodes in N, and replace every edge in        // Check that none of the nodes to be merged is a leaf , and that they are all at the same depth .// to the unzipped node itself , which ( by construction ) will be the first one in the returned vector .

24
// Since we want the actual nodes ( and not just the nodes above them in their chains ) to be merged ,

25
// we add them to the ends of their respective chains here as well .

Figure 1 :Figure 2 :
Figure 1: Adding a node to a hierarchical selection (red nodes are those that are explicitly stored in the representation, pink nodes are those that are implicitly selected because one of their ancestors is in the representation): (a) the node to be added is highlighted in green; (b) any selected descendants of the node being added are removed from the representation; (c) the new node is added to the representation, and the nodes to be checked during consolidation are circled in blue; (d) the parent of the added node is consolidated; (e) the grandparent of the added node is consolidated. 3

Lemma 6 .
The regions of the 'current' nodes used during single-node unzipping of a node n to a depth d min are all equal to the region of n, i.e. ∀d ∈[d min , D(n)] .(R(cur d ) = R(n)).Proof.As per the definition in the main paper, cur D(n) = n, so the base case is clearly true.Moreover, for d min ≤ d < D(n), cur d = π H d (cur d+1 ), i.e. cur d is the parent of cur d+1 in H d , the hierarchy generated by removing the parent of cur d+1 in H d+1 and replacing it with the nodes in splitPar H d+1 ({cur d+1 }).By definition, this latter set contains a single node with a region of R(n), which is the node that will become cur d , together with a number of other nodes corresponding to the connected components of what is left of the original parent region.The stated result thus follows directly from the definition of splitPar.Lemma 7. The nodes added to chains d during single-node unzipping of a node n are precisely those in splitAnc H (d, {n}).

Proof.
By definition, chain zipping replaces N C d with merge(N C d ), for each d such that D min (N C ) ≤ d ≤ D max (N C ).In the case of the chains returned by unzipping a node n to a depth d min in a hierarchy H, D min (N C ) = d min and D max (N C ) = D(n) − 1.Given Lemma 9, zipping those chains back together replaces splitAnc H (d, {n}) with ψ d H (n) at each d such that d min ≤ d < D(n).This reverts the earlier unzip.
(b), in which the set of nodes N being merged are circled in green, the nodes in N m are shown in red, and the other colours indicate connected components of N.

Figure 4 :
Figure 4: Examples of (a) parent switching and (b) non-sibling node merging, showing the nodes that will be modified in each case.In (a), the parent of the green leaf is being switched to the node circled in yellow, nodes in N u are shown in red and nodes in N z are shown in orange (see text for details).In (b), the set of nodes N being merged are circled in green, the nodes that will be modified are shown in red, and the other colours indicate connected components of N.

O
(|N|(D(N) − d min )C s ).Having unzipped all of the nodes, non-sibling node merging then zips the nodes in each component together again.Since the zip for each component involves O((D(N) − d min )C m ) work, the total cost of the zips involved in the non-sibling node merge is maximised when there are as many zips as possible.In the worst case, there can be O(|N|) such components, so the total cost of the zips is thusO(|N|(D(N) − d min )C m ).Combining these two gives us the overall cost of the non-sibling node merge, namelyO(|N|(D(N) − d min )(C s + C m )).Parent Switching.Consider a parent switch operation that switches the parent of a node n to a new parent p.As per the definition of parent switching, this involves unzipping n to depth d min = D(ψ + ({n, p})) + 1, and then zipping the resulting chain leading down to the parent of n to the chain leading down to p.There is thus potentially one split and one merge operation at each depth d ∈ [d min , D(n)), leading to a complexity of:O((D(n) − d min )(C s + C m )) e. the edges joining nodes in N to nodes not in N, with a new edge connecting m to the endpoint of the edge that is not in N. Assuming E D(N) H is represented as a hash set, the expected complexity of this is O(|E D(N) H [N, ∪]|).The edge updates are the dominant cost of the merge operation, so O(|E D(N) H [N, ∪]|) is also the expected complexity of sibling node merging as a whole.Listing 1 Single-Node Unzipping 1 function unzip_node ( node : NodeID , toDepth : Int ) → Vector < Chain > 2 // Initialise the array of chains with a singleton chain containing only the node being unzipped .the connected components of the current node ' s siblings .9 var ccs : Vector < Set < NodeID > > := f i n d _ c o n n e c t e d _ c o m p o n e n t s ( siblings_of ( cur )); 10 11 // Add in the component { cur } and split the current node ' s parent .
Multi-Node Unzipping 1 function unzip_select io n ( sel : Selection , toDepth : Int ) → Map < NodeID , Chain > 2 var chains : Map < NodeID , Chain >; the nodes in the selection , starting with those deepest in the hierarchy .5 var nodesByDepth : Map < Int , Set < NodeID > > := sel .g r o u p _ n o d e s _ b y _ d e p t h (); 6 var depth : Int := nodesByDepth .max_key (); 7 var curs : Set < NodeID > := nodesByDepth [ depth ]; the current nodes by parent .10 var p a r e n t T o S e l e c t e d C h i l d M a p : Map < NodeID , Set < NodeID > >; 11 for cur : NodeID ∈ curs 12 p a r e n t T o S e l e c t e d C h i l d M a p [ parent_of ( cur )].insert ( cur ); 13 14 // Split each parent node in turn .15 var result : Set < NodeID >; 16 for ( parent , sel e c t e d C h i l d r e n ) ∈ p a r e n t T o S e l e c t e d C h i l d M a p 17 var siblings : Set < NodeID > := siblings_of ( se l e c t e d C h i l d r e n ); 18 var ccs : Vector < Set < NodeID > > := f i n d _ c o n n e c t e d _ c o m p o n e n t s ( siblings ); 19 ccs .append ( f i n d _ c o n n e c t e d _ c o m p o n e n t s ( s e l e c t e d C h i l d r e n )); 20 result .append ( split_node ( parent , ccs )); 21 22// Prepend each existing chain with its head node ' s parent , and remove that parent from the split results .
a new singleton chain for each remaining node in the split results .
the current nodes and the depth .
curs := parents_of ( curs ); in any new nodes from the selection whose depth we have now reached .curs .append ( nodesByDepth [ depth ]); 38 39 return chains ; var nodes := Vector < Set < NodeID > >( minChainSize ); 18 for i := 0 up to minChainSize -1 19 for each chain ∈ chains 20 if i < chain .size () then nodes [ i ]. insert ( chain [ i ]); 21 if not are_connected ( nodes [ i ]) then throw ; 22 23 // Merge the nodes at each level , starting from the highest .24for i := 0 up to minChainSize -1 25 m e r g e _s i b l i n g _ n o d e s ( nodes [ i ]); Listing 4 Non-Sibling Node Merging 1 function m e r g e _ n o n s i b l i n g _ n o d e s ( nodes : Set < NodeID >) → Set < NodeID > 2 // Check that there are nodes to be merged .

6
var commonDepth : Int := l o w e s t _ i n d e x e d _ n o d e ( nodes ).depth (); 7 for each n : NodeID ∈ nodes 8 if is_leaf ( n ) or n .depth () commonDepth then throw ; the connected components of the nodes to be merged .13 var ccs := f i n d _ c o n n e c t e d _ c o m p o n e n t s ( nodes ); 14 15 // Merge the nodes in each connected component of size > 1. 16 for each cc : Set < NodeID > ∈ ccs 17 if cc .size () = 1 then continue ; // nothing to do 18 19 // Find the depth to which the nodes need to be unzipped .20 var toDepth : Int := f i n d _ c o m m o n _ a n c e s t o r _ d e p t h ( cc ) + 1; 21 22 // Unzip each node in the component to the specified depth , in each case keeping the chain corresponding 23 the chains together to effect the merge .34 mergedNodes .insert ( zip_chains ( chains )); 35 36 return mergedNodes ; .e. that only nodes at depth d + 1 or greater have changed by the time we get to hierarchy H d+1 in the unzip process.Now, recall from the main paper that we use N C d to refer to the nodes in a set of chains C that are at depth d.As a result of Lemma 7, we can now assert the following: Corollary 8.If C is the set of chains resulting from a singlenode unzipping of a node n to a depth d min in hierarchy H, then: