docs: def a_star(sink, heuristic_source, sources, machine, wrap_around):
      """Use A* to find a path from any of the sources to the sink.
  
      Note that the heuristic means that the search will proceed towards
      heuristic_source without any concern for any other sources. This means that
      the algorithm may miss a very close neighbour in order to pursue its goal
      of reaching heuristic_source. This is not considered a problem since 1) the
      heuristic source will typically be in the direction of the rest of the tree
      and near by and often the closest entity 2) it prevents us accidentally
      forming loops in the rest of the tree since we'll stop as soon as we touch
      any part of it.
  
      Parameters
      ----------
      sink : (x, y)
      heuristic_source : (x, y)
          An element from `sources` which is used as a guiding heuristic for the
          A* algorithm.
      sources : set([(x, y), ...])
      machine : :py:class:`~rig.place_and_route.Machine`
      wrap_around : bool
          Consider wrap-around links in heuristic distance calculations.
  
      Returns
      -------
      [(:py:class:`~rig.routing_table.Routes`, (x, y)), ...]
          A path starting with a coordinate in `sources` and terminating at
          connected neighbour of `sink` (i.e. the path does not include `sink`).
          The direction given is the link down which to proceed from the given
          (x, y) to arrive at the next point in the path.
  
      Raises
      ------
      :py:class:~rig.place_and_route.exceptions.MachineHasDisconnectedSubregion`
          If a path cannot be found.
      """
      # Select the heuristic function to use for distances
      if wrap_around:
          heuristic = (lambda node:
                       shortest_torus_path_length(to_xyz(node),
                                                  to_xyz(heuristic_source),
                                                  machine.width, machine.height))
      else:
          heuristic = (lambda node:
                       shortest_mesh_path_length(to_xyz(node),
                                                 to_xyz(heuristic_source)))
  
      # A dictionary {node: (direction, previous_node}. An entry indicates that
      # 1) the node has been visited and 2) which node we hopped from (and the
      # direction used) to reach previous_node.  This may be None if the node is
      # the sink.
      visited = {sink: None}
  
      # The node which the tree will be reconnected to
      selected_source = None
  
      # A heap (accessed via heapq) of (distance, (x, y)) where distance is the
      # distance between (x, y) and heuristic_source and (x, y) is a node to
      # explore.
      to_visit = [(heuristic(sink), sink)]
      while to_visit:
          _, node = heapq.heappop(to_visit)
  
          # Terminate if we've found the destination
          if node in sources:
              selected_source = node
              break
  
          # Try all neighbouring locations. Note: link identifiers are from the
          # perspective of the neighbour, not the current node!
          for neighbour_link in Links:
              vector = neighbour_link.opposite.to_vector()
              neighbour = ((node[0] + vector[0]) % machine.width,
                           (node[1] + vector[1]) % machine.height)
  
              # Skip links which are broken
              if (neighbour[0], neighbour[1], neighbour_link) not in machine:
                  continue
  
              # Skip neighbours who have already been visited
              if neighbour in visited:
                  continue
  
              # Explore all other neighbours
              visited[neighbour] = (neighbour_link, node)
              heapq.heappush(to_visit, (heuristic(neighbour), neighbour))
  
      # Fail of no paths exist
      if selected_source is None:
          raise MachineHasDisconnectedSubregion(
              "Could not find path from {} to {}".format(
                  sink, heuristic_source))
  
      # Reconstruct the discovered path, starting from the source we found and
      # working back until the sink.
      path = [(Routes(visited[selected_source][0]), selected_source)]
      while visited[path[-1][1]][1] != sink:
          node = visited[path[-1][1]][1]
          direction = Routes(visited[node][0])
          path.append((direction, node))
  
      return path