docs: def ordered_covering(routing_table, target_length, aliases=dict(),
                       no_raise=False):
      """Reduce the size of a routing table by merging together entries where
      possible.
  
      .. warning::
  
          The input routing table *must* also include entries which could be
          removed and replaced by default routing.
  
      .. warning::
  
          It is assumed that the input routing table is not in any particular
          order and may be reordered into ascending order of generality (number
          of don't cares/Xs in the key-mask) without affecting routing
          correctness.  It is also assumed that if this table is unordered it is
          at least orthogonal (i.e., there are no two entries which would match
          the same key) and reorderable.
  
          .. note::
  
              If *all* the keys in the table are derived from a single instance
              of :py:class:`~rig.bitfield.BitField` then the table is guaranteed
              to be orthogonal and reorderable.
  
          .. note::
  
              Use :py:meth:`~rig.routing_table.expand_entries` to generate an
              orthogonal table and receive warnings if the input table is not
              orthogonal.
  
      Parameters
      ----------
      routing_table : [:py:class:`~rig.routing_table.RoutingTableEntry`, ...]
          Routing entries to be merged.
      target_length : int or None
          Target length of the routing table; the minimisation procedure will
          halt once either this target is reached or no further minimisation is
          possible. If None then the table will be made as small as possible.
  
      Other Parameters
      ----------------
      aliases : {(key, mask): {(key, mask), ...}, ...}
          Dictionary of which keys and masks in the routing table are
          combinations of other (now removed) keys and masks; this allows us to
          consider only the keys and masks the user actually cares about when
          determining if inserting a new entry will break the correctness of the
          table. This should be supplied when using this method to update an
          already minimised table.
      no_raise : bool
          If False (the default) then an error will be raised if the table cannot
          be minimised to be smaller than `target_length` and `target_length` is
          not None. If True then a table will be returned regardless of the size
          of the final table.
  
      Raises
      ------
      MinimisationFailedError
          If the smallest table that can be produced is larger than
          `target_length` and `no_raise` is False.
  
      Returns
      -------
      [:py:class:`~rig.routing_table.RoutingTableEntry`, ...]
          Reduced routing table entries.
      {(key, mask): {(key, mask), ...}, ...}
          A new aliases dictionary.
      """
      # Copy the aliases dictionary
      aliases = dict(aliases)
  
      # Perform an initial sort of the routing table in order of increasing
      # generality.
      routing_table = sorted(
          routing_table,
          key=lambda entry: _get_generality(entry.key, entry.mask)
      )
  
      while target_length is None or len(routing_table) > target_length:
          # Get the best merge
          merge = _get_best_merge(routing_table, aliases)
  
          # If there is no merge then stop
          if merge.goodness <= 0:
              break
  
          # Otherwise apply the merge, this returns a new routing table and a new
          # aliases dictionary.
          routing_table, aliases = merge.apply(aliases)
  
      # If the table is still too big then raise an error
      if (not no_raise and
              target_length is not None and
              len(routing_table) > target_length):
          raise MinimisationFailedError(target_length, len(routing_table))
  
      # Return the finished routing table and aliases table
      return routing_table, aliases