docs: def ner_net(source, destinations, width, height, wrap_around=False, radius=10):
      """Produce a shortest path tree for a given net using NER.
  
      This is the kernel of the NER algorithm.
  
      Parameters
      ----------
      source : (x, y)
          The coordinate of the source vertex.
      destinations : iterable([(x, y), ...])
          The coordinates of destination vertices.
      width : int
          Width of the system (nodes)
      height : int
          Height of the system (nodes)
      wrap_around : bool
          True if wrap-around links should be used, false if they should be
          avoided.
      radius : int
          Radius of area to search from each node. 20 is arbitrarily selected in
          the paper and shown to be acceptable in practice.
  
      Returns
      -------
      (:py:class:`~.rig.place_and_route.routing_tree.RoutingTree`,
       {(x,y): :py:class:`~.rig.place_and_route.routing_tree.RoutingTree`, ...})
          A RoutingTree is produced rooted at the source and visiting all
          destinations but which does not contain any vertices etc. For
          convenience, a dictionarry mapping from destination (x, y) coordinates
          to the associated RoutingTree is provided to allow the caller to insert
          these items.
      """
      # Map from (x, y) to RoutingTree objects
      route = {source: RoutingTree(source)}
  
      # Handle each destination, sorted by distance from the source, closest
      # first.
      for destination in sorted(destinations,
                                key=(lambda destination:
                                     shortest_mesh_path_length(
                                         to_xyz(source), to_xyz(destination))
                                     if not wrap_around else
                                     shortest_torus_path_length(
                                         to_xyz(source), to_xyz(destination),
                                         width, height))):
          # We shall attempt to find our nearest neighbouring placed node.
          neighbour = None
  
          # Try to find a nearby (within radius hops) node in the routing tree
          # that we can route to (falling back on just routing to the source).
          #
          # In an implementation according to the algorithm's original
          # specification looks for nodes at each point in a growing set of rings
          # of concentric hexagons. If it doesn't find any destinations this
          # means an awful lot of checks: 1261 for the default radius of 20.
          #
          # An alternative (but behaviourally identical) implementation scans the
          # list of all route nodes created so far and finds the closest node
          # which is < radius hops (falling back on the origin if no node is
          # closer than radius hops).  This implementation requires one check per
          # existing route node. In most routes this is probably a lot less than
          # 1261 since most routes will probably have at most a few hundred route
          # nodes by the time the last destination is being routed.
          #
          # Which implementation is best is a difficult question to answer:
          # * In principle nets with quite localised connections (e.g.
          #   nearest-neighbour or centroids traffic) may route slightly more
          #   quickly with the original algorithm since it may very quickly find
          #   a neighbour.
          # * In nets which connect very spaced-out destinations the second
          #   implementation may be quicker since in such a scenario it is
          #   unlikely that a neighbour will be found.
          # * In extremely high-fan-out nets (e.g. broadcasts), the original
          #   method is very likely to perform *far* better than the alternative
          #   method since most iterations will complete immediately while the
          #   alternative method must scan *all* the route vertices.
          # As such, it should be clear that neither method alone is 'best' and
          # both have degenerate performance in certain completely reasonable
          # styles of net. As a result, a simple heuristic is used to decide
          # which technique to use.
          #
          # The following micro-benchmarks are crude estimate of the
          # runtime-per-iteration of each approach (at least in the case of a
          # torus topology)::
          #
          #     $ # Original approach
          #     $ python -m timeit --setup 'x, y, w, h, r = 1, 2, 5, 10, \
          #                                     {x:None for x in range(10)}' \
          #                        'x += 1; y += 1; x %= w; y %= h; (x, y) in r'
          #     1000000 loops, best of 3: 0.207 usec per loop
          #     $ # Alternative approach
          #     $ python -m timeit --setup 'from rig.geometry import \
          #                                 shortest_torus_path_length' \
          #                        'shortest_torus_path_length( \
          #                             (0, 1, 2), (3, 2, 1), 10, 10)'
          #     1000000 loops, best of 3: 0.666 usec per loop
          #
          # From this we can approximately suggest that the alternative approach
          # is 3x more expensive per iteration. A very crude heuristic is to use
          # the original approach when the number of route nodes is more than
          # 1/3rd of the number of routes checked by the original method.
          concentric_hexagons = memoized_concentric_hexagons(radius)
          if len(concentric_hexagons) < len(route) / 3:
              # Original approach: Start looking for route nodes in a concentric
              # spiral pattern out from the destination node.
              for x, y in concentric_hexagons:
                  x += destination[0]
                  y += destination[1]
                  if wrap_around:
                      x %= width
                      y %= height
                  if (x, y) in route:
                      neighbour = (x, y)
                      break
          else:
              # Alternative approach: Scan over every route node and check to see
              # if any are < radius, picking the closest one if so.
              neighbour = None
              neighbour_distance = None
              for candidate_neighbour in route:
                  if wrap_around:
                      distance = shortest_torus_path_length(
                          to_xyz(candidate_neighbour), to_xyz(destination),
                          width, height)
                  else:
                      distance = shortest_mesh_path_length(
                          to_xyz(candidate_neighbour), to_xyz(destination))
  
                  if distance <= radius and (neighbour is None or
                                             distance < neighbour_distance):
                      neighbour = candidate_neighbour
                      neighbour_distance = distance
  
          # Fall back on routing directly to the source if no nodes within radius
          # hops of the destination was found.
          if neighbour is None:
              neighbour = source
  
          # Find the shortest vector from the neighbour to this destination
          if wrap_around:
              vector = shortest_torus_path(to_xyz(neighbour),
                                           to_xyz(destination),
                                           width, height)
          else:
              vector = shortest_mesh_path(to_xyz(neighbour), to_xyz(destination))
  
          # The longest-dimension-first route may inadvertently pass through an
          # already connected node. If the route is allowed to pass through that
          # node it would create a cycle in the route which would be VeryBad(TM).
          # As a result, we work backward through the route and truncate it at
          # the first point where the route intersects with a connected node.
          ldf = longest_dimension_first(vector, neighbour, width, height)
          i = len(ldf)
          for direction, (x, y) in reversed(ldf):
              i -= 1
              if (x, y) in route:
                  # We've just bumped into a node which is already part of the
                  # route, this becomes our new neighbour and we truncate the LDF
                  # route. (Note ldf list is truncated just after the current
                  # position since it gives (direction, destination) pairs).
                  neighbour = (x, y)
                  ldf = ldf[i + 1:]
                  break
  
          # Take the longest dimension first route.
          last_node = route[neighbour]
          for direction, (x, y) in ldf:
              this_node = RoutingTree((x, y))
              route[(x, y)] = this_node
  
              last_node.children.append((Routes(direction), this_node))
              last_node = this_node
  
      return (route[source], route)