Return a list of (target_id, display_name) tuples.

    By default original names are passed as-is, only indices are added:
    >>> get_target_display_names(['x', 'y'])
    [(0, 'x'), (1, 'y')]

    ``targets`` can be written using both names from ``target_names` and
    from ``original_names``:
    >>> get_target_display_names(['x', 'y'], targets=['y', 'X'],
    ...                   target_names={'x': 'X'})
    [(1, 'y'), (0, 'X')]

    Provide display names:
    >>> get_target_display_names([0, 2], target_names=['foo', 'bar'])
    [(0, 'foo'), (1, 'bar')]

    Change order of labels:
    >>> get_target_display_names(['x', 'y'], targets=['y', 'x'])
    [(1, 'y'), (0, 'x')]

    Provide display names, choose only a subset of labels:
    >>> get_target_display_names([0, 2], target_names=['foo', 'bar'], targets=[2])
    [(1, 'bar')]

    >>> get_target_display_names([False, True], targets=[True])
    [(1, True)]

    >>> get_target_display_names([False, True], targets=[False])
    [(0, False)]

    target_names can be a dictionary with {old_name: new_name} labels:
    >>> get_target_display_names(['x', 'y'], targets=['y', 'x'],
    ...                   target_names={'x': 'X'})
    [(1, 'y'), (0, 'X')]

    Error is raised when target_names format is invalid:
    >>> get_target_display_names(['x', 'y'], target_names=['foo'])
    Traceback (most recent call last):
    ...
    ValueError: target_names must have the same length as original names (expected 2, got 1)

    Top target selection by score:
    >>> get_target_display_names(['x', 'y', 'z'], score=[1, 2, 1.5], top_targets=2)
    [(1, 'y'), (2, 'z')]

    Top target selection by score, negative:
    >>> get_target_display_names(['x', 'y', 'z'], score=[1, 2, 1.5], top_targets=-3)
    [(0, 'x'), (2, 'z'), (1, 'y')]

    Error is raised if both top_targets and targets are passed:
    >>> get_target_display_names(['x', 'y'], targets=['x'], score=[1, 2], top_targets=1)
    Traceback (most recent call last):
    ...
    ValueError: Pass either "targets" or "top_targets", not both

def get_target_display_names(original_names=None, target_names=None,
                             targets=None, top_targets=None, score=None):
    """
    Return a list of (target_id, display_name) tuples.

    By default original names are passed as-is, only indices are added:
    >>> get_target_display_names(['x', 'y'])
    [(0, 'x'), (1, 'y')]

    ``targets`` can be written using both names from ``target_names` and
    from ``original_names``:
    >>> get_target_display_names(['x', 'y'], targets=['y', 'X'],
    ...                   target_names={'x': 'X'})
    [(1, 'y'), (0, 'X')]

    Provide display names:
    >>> get_target_display_names([0, 2], target_names=['foo', 'bar'])
    [(0, 'foo'), (1, 'bar')]

    Change order of labels:
    >>> get_target_display_names(['x', 'y'], targets=['y', 'x'])
    [(1, 'y'), (0, 'x')]

    Provide display names, choose only a subset of labels:
    >>> get_target_display_names([0, 2], target_names=['foo', 'bar'], targets=[2])
    [(1, 'bar')]

    >>> get_target_display_names([False, True], targets=[True])
    [(1, True)]

    >>> get_target_display_names([False, True], targets=[False])
    [(0, False)]

    target_names can be a dictionary with {old_name: new_name} labels:
    >>> get_target_display_names(['x', 'y'], targets=['y', 'x'],
    ...                   target_names={'x': 'X'})
    [(1, 'y'), (0, 'X')]

    Error is raised when target_names format is invalid:
    >>> get_target_display_names(['x', 'y'], target_names=['foo'])
    Traceback (most recent call last):
    ...
    ValueError: target_names must have the same length as original names (expected 2, got 1)

    Top target selection by score:
    >>> get_target_display_names(['x', 'y', 'z'], score=[1, 2, 1.5], top_targets=2)
    [(1, 'y'), (2, 'z')]

    Top target selection by score, negative:
    >>> get_target_display_names(['x', 'y', 'z'], score=[1, 2, 1.5], top_targets=-3)
    [(0, 'x'), (2, 'z'), (1, 'y')]

    Error is raised if both top_targets and targets are passed:
    >>> get_target_display_names(['x', 'y'], targets=['x'], score=[1, 2], top_targets=1)
    Traceback (most recent call last):
    ...
    ValueError: Pass either "targets" or "top_targets", not both
    """
    if isinstance(target_names, (list, tuple, np.ndarray)):
        if original_names is not None:
            if len(target_names) != len(original_names):
                raise ValueError("target_names must have the same length as "
                                 "original names (expected {}, got {})".format(
                                     len(original_names), len(target_names)
                                 ))
        display_names = target_names
    elif isinstance(target_names, dict):
        display_names = [target_names.get(name, name)
                         for name in original_names]
    else:
        display_names = original_names

    if targets is None:
        if top_targets is not None:
            assert len(score) == len(original_names)
            if top_targets < 0:
                reverse = False
                top_targets = -top_targets
            else:
                reverse = True
            targets = [
                target for _, target in sorted(
                    enumerate(original_names),
                    key=lambda x: score[x[0]],
                    reverse=reverse,
                )][:top_targets]
        else:
            targets = original_names
    elif top_targets is not None:
        raise ValueError('Pass either "targets" or "top_targets", not both')

    target_indices = _get_value_indices(original_names, display_names, targets)
    names = [display_names[i] for i in target_indices]
    return list(zip(target_indices, names))