Index of Rightmost Set Bit

l
p
# Reference: https://www.geeksforgeeks.org/position-of-rightmost-set-bit/


def get_index_of_rightmost_set_bit(number: int) -> int:
    """
    Take in a positive integer 'number'.
    Returns the zero-based index of first set bit in that 'number' from right.
    Returns -1, If no set bit found.

    >>> get_index_of_rightmost_set_bit(0)
    -1
    >>> get_index_of_rightmost_set_bit(5)
    0
    >>> get_index_of_rightmost_set_bit(36)
    2
    >>> get_index_of_rightmost_set_bit(8)
    3
    >>> get_index_of_rightmost_set_bit(-18)
    Traceback (most recent call last):
        ...
    ValueError: Input must be a non-negative integer
    >>> get_index_of_rightmost_set_bit('test')
    Traceback (most recent call last):
        ...
    ValueError: Input must be a non-negative integer
    >>> get_index_of_rightmost_set_bit(1.25)
    Traceback (most recent call last):
        ...
    ValueError: Input must be a non-negative integer
    """

    if not isinstance(number, int) or number < 0:
        raise ValueError("Input must be a non-negative integer")

    intermediate = number & ~(number - 1)
    index = 0
    while intermediate:
        intermediate >>= 1
        index += 1
    return index - 1


if __name__ == "__main__":
    """
    Finding the index of rightmost set bit has some very peculiar use-cases,
    especially in finding missing or/and repeating numbers in a list of
    positive integers.
    """
    import doctest

    doctest.testmod(verbose=True)