docs: def _x_visit(H, source_node, b_visit):
      """General form of the B-Visit algorithm, extended to also perform
      an implicit F-Visit if the b_visit flag is not set (providing better
      time/memory performance than explcitily taking the hypergraph's
      symmetric image and then performing the B-Visit on that).
  
      Refer to 'b_visit's or 'f_visit's documentation for more details.
  
      :param H: the hypergraph to perform the 'B-Visit' algorithm on.
      :param source_node: the initial node to begin traversal from.
      :param b_visit: boolean flag representing whether a B-Visit should
                      be performed (vs an F-Visit).
      :returns: set -- nodes that were x-visited in this traversal.
                dict -- mapping from each node visited to the ID of the hyperedge
                      that preceeded it in this traversal.
                dict -- mapping from each hyperedge ID to the node that preceeded
                      it in this traversal.
                dict -- mapping from each node to an integer representing the
                      cardinality of the path from the source node to that node.
      :raises: TypeError -- Algorithm only applicable to directed hypergraphs
  
      """
      if not isinstance(H, DirectedHypergraph):
          raise TypeError("Algorithm only applicable to directed hypergraphs")
  
      # If the b_visit flag is set, perform a traditional B-Visit
      if b_visit:
          forward_star = H.get_forward_star
          hyperedge_tail = H.get_hyperedge_tail
          hyperedge_head = H.get_hyperedge_head
      # If the b_visit flag is not set, implicitly perform an F-Visit by
      # implicitly taking the symmetric image (what the 'else' statement
      # is for) and then performing a traditional B-Visit
      else:
          forward_star = H.get_backward_star
          hyperedge_tail = H.get_hyperedge_head
          hyperedge_head = H.get_hyperedge_tail
  
      node_set = H.get_node_set()
      # Pv keeps track of the ID of the hyperedge that directely
      # preceeded each node in the traversal
      Pv = {node: None for node in node_set}
  
      # v keeps track of the cardinality of the path from the source node to
      # any other B-connected node ('inf' cardinality for non-B-connected nodes)
      v = {node: float("inf") for node in node_set}
      v[source_node] = 0
  
      hyperedge_id_set = H.get_hyperedge_id_set()
      # Pe keeps track of the node that directedly preceeded
      # each hyperedge in the traversal
      Pe = {hyperedge_id: None for hyperedge_id in hyperedge_id_set}
  
      # k keeps track of how many nodes in the tail of each hyperedge are
      # B-connected (when all nodes in a tail are B-connected, that hyperedge
      # can then be traversed)
      k = {hyperedge_id: 0 for hyperedge_id in hyperedge_id_set}
  
      # Explicitly tracks the set of B-visited nodes
      x_visited_nodes = set([source_node])
  
      Q = Queue()
      Q.put(source_node)
  
      while not Q.empty():
          current_node = Q.get()
          # At current_node, we can traverse each hyperedge in its forward star
          for hyperedge_id in forward_star(current_node):
              # Since we're arrived at a new node, we increment
              # k[hyperedge_id] to indicate that we've reached 1 new
              # node in this hyperedge's tail
              k[hyperedge_id] += 1
              # Traverse this hyperedge only when we have reached all the nodes
              # in its tail (i.e., when k[hyperedge_id] == |T(hyperedge_id)|)
              if k[hyperedge_id] == len(hyperedge_tail(hyperedge_id)):
                  Pe[hyperedge_id] = current_node
                  # Traversing the hyperedge yields the set of head nodes of
                  # the hyperedge; B-visit each head node
                  for head_node in hyperedge_head(hyperedge_id):
                      if head_node in x_visited_nodes:
                          continue
                      Pv[head_node] = hyperedge_id
                      Q.put(head_node)
                      v[head_node] = v[Pe[hyperedge_id]] + 1
                      x_visited_nodes.add(head_node)
  
      return x_visited_nodes, Pv, Pe, v