code: def nni(
              self,
              edge,
              head_subtree,
              tail_subtree,
      ):
          
  
          # This implementation works on unrooted Trees. If the input Tree is
          # rooted, the ReversibleDeroot decorator will temporarily unroot the
          # tree while the NNI is carried out
  
          original_seed = self.tree._tree.seed_node
          head = edge.head_node
          tail = edge.tail_node
          self.tree._tree.reseed_at(tail)
          try:
              assert head_subtree.parent_node == head
              assert tail_subtree.parent_node == tail
          except:
              print(head, tail, head_subtree, tail_subtree)
              raise
          head.remove_child(head_subtree)
          tail.remove_child(tail_subtree)
          head.add_child(tail_subtree)
          tail.add_child(head_subtree)
          self.tree._tree.reseed_at(original_seed)
          self.tree._tree.encode_bipartitions()
          self.tree._dirty = True
docs: *Inplace* Nearest-neighbour interchange (NNI) operation.
  
          An edge in the tree has two or more subtrees at each end (ends are
          designated 'head' and 'tail'). The NNI operation exchanges one of the
          head subtrees for one of the tail subtrees, as follows:
  
              A      C                        C      A    | Subtree A is exchanged
               \    /        +NNI(A,C)         \    /     | with subtree C.
                --->        ==========>         --->      |
               /    \                          /    \     |
              B      D                        B      D