# Path: uqbar/containers/UniqueTreeContainer.py
# class UniqueTreeContainer(UniqueTreeNode):
# 
#     ### INITIALIZER ###
# 
#     def __init__(self, children=None, name=None):
#         UniqueTreeNode.__init__(self, name=name)
#         self._named_children = {}
# 
#     ### SPECIAL METHODS ###
# 
#     def __contains__(self, expr):
#         if isinstance(expr, str):
#             return expr in self._named_children
#         for x in self._children:
#             if x is expr:
#                 return True
#         return False
# 
#     def __iter__(self):
#         for child in self._children:
#             yield child
# 
#     def __len__(self):
#         return len(self._children)
# 
#     ### PRIVATE METHODS ###
# 
#     def _cache_named_children(self):
#         name_dictionary = super()._cache_named_children()
#         if hasattr(self, "_named_children"):
#             for name, children in self._named_children.items():
#                 name_dictionary[name] = copy.copy(children)
#         return name_dictionary
# 
#     ### PRIVATE PROPERTIES ###
# 
#     @property
#     def _node_class(self):
#         return UniqueTreeNode
# 
#     ### PUBLIC METHODS ###
# 
#     def depth_first(self, top_down=True, prototype=None):
#         for child in tuple(self):
#             if top_down:
#                 if not prototype or isinstance(child, prototype):
#                     yield child
#             if isinstance(child, UniqueTreeContainer):
#                 yield from child.depth_first(top_down=top_down, prototype=prototype)
#             if not top_down:
#                 if not prototype or isinstance(child, prototype):
#                     yield child
# 
#     def recurse(self, prototype=None):
#         return self.depth_first(prototype=prototype)
# 
#     ### PUBLIC PROPERTIES ###
# 
#     @property
#     def children(self):
#         return tuple(self._children)


import collections
import threading
from .UniqueTreeContainer import UniqueTreeContainer
            if new_node in self._children_reversed:
                # pre-existing node
                self._children.pop(self._children_reversed[new_node])
            self._children[new_key] = new_node
            self._children_reversed[new_node] = new_key
        return new_nodes, old_nodes

    def _update_parentage(self, new_nodes, old_nodes):
        for old_node in old_nodes:
            old_node._set_parent(None)
        for new_node in new_nodes:
            new_node._set_parent(self)

    def _validate(self, new_items, old_items):
        parentage = self.parentage
        for _, new_node in new_items:
            if not isinstance(new_node, self._node_class):
                raise ValueError(f"Expected {self._node_class}, got {type(new_node)}")
            elif new_node in parentage:
                raise ValueError("Cannot set parent node as child.")

    ### PUBLIC METHODS ###

    def clear(self):
        self._mutate([], list(self.items()))

    def depth_first(self, top_down=True):
        for child in tuple(self.values()):
            if top_down:
                yield child

# Path: uqbar/containers/UniqueTreeContainer.py
# class UniqueTreeContainer(UniqueTreeNode):
# 
#     ### INITIALIZER ###
# 
#     def __init__(self, children=None, name=None):
#         UniqueTreeNode.__init__(self, name=name)
#         self._named_children = {}
# 
#     ### SPECIAL METHODS ###
# 
#     def __contains__(self, expr):
#         if isinstance(expr, str):
#             return expr in self._named_children
#         for x in self._children:
#             if x is expr:
#                 return True
#         return False
# 
#     def __iter__(self):
#         for child in self._children:
#             yield child
# 
#     def __len__(self):
#         return len(self._children)
# 
#     ### PRIVATE METHODS ###
# 
#     def _cache_named_children(self):
#         name_dictionary = super()._cache_named_children()
#         if hasattr(self, "_named_children"):
#             for name, children in self._named_children.items():
#                 name_dictionary[name] = copy.copy(children)
#         return name_dictionary
# 
#     ### PRIVATE PROPERTIES ###
# 
#     @property
#     def _node_class(self):
#         return UniqueTreeNode
# 
#     ### PUBLIC METHODS ###
# 
#     def depth_first(self, top_down=True, prototype=None):
#         for child in tuple(self):
#             if top_down:
#                 if not prototype or isinstance(child, prototype):
#                     yield child
#             if isinstance(child, UniqueTreeContainer):
#                 yield from child.depth_first(top_down=top_down, prototype=prototype)
#             if not top_down:
#                 if not prototype or isinstance(child, prototype):
#                     yield child
# 
#     def recurse(self, prototype=None):
#         return self.depth_first(prototype=prototype)
# 
#     ### PUBLIC PROPERTIES ###
# 
#     @property
#     def children(self):
#         return tuple(self._children)


# Path: uqbar/containers/UniqueTreeDict.py
import collections
import threading
from .UniqueTreeContainer import UniqueTreeContainer

            if new_node in self._children_reversed:
                # pre-existing node
                self._children.pop(self._children_reversed[new_node])
            self._children[new_key] = new_node
            self._children_reversed[new_node] = new_key
        return new_nodes, old_nodes

    def _update_parentage(self, new_nodes, old_nodes):
        for old_node in old_nodes:
            old_node._set_parent(None)
        for new_node in new_nodes:
            new_node._set_parent(self)

    def _validate(self, new_items, old_items):
        parentage = self.parentage
        for _, new_node in new_items:
            if not isinstance(new_node, self._node_class):
                raise ValueError(f"Expected {self._node_class}, got {type(new_node)}")
            elif new_node in parentage:
                raise ValueError("Cannot set parent node as child.")

    ### PUBLIC METHODS ###

    def clear(self):
        self._mutate([], list(self.items()))

    def depth_first(self, top_down=True):
        for child in tuple(self.values()):
            if top_down:
                yield child

            if isinstance(child, UniqueTreeContainer):