Apply a caesar cipher to a string.

    The caesar cipher is a substition cipher where each letter in the given
    alphabet is replaced by a letter some fixed number down the alphabet.

    If ``shift`` is ``1``, *A* will become *B*, *B* will become *C*, etc...

    You can define the alphabets that will be shift by specifying one or more
    shift ranges. The characters will than be shifted within the given ranges.

    Args:
        shift(int): The shift to apply.
        data(str): The string to apply the cipher to.
        shift_ranges(list of str): Which alphabets to shift.

    Returns:
        str: The string with the caesar cipher applied.

    Examples:
        >>> caesar(16, 'Pwnypack')
        'Fmdofqsa'
        >>> caesar(-16, 'Fmdofqsa')
        'Pwnypack'
        >>> caesar(16, 'PWNYpack', shift_ranges=('AZ',))
        'FMDOpack'
        >>> caesar(16, 'PWNYpack', shift_ranges=('Az',))
        '`g^iFqsA'

def caesar(shift, data, shift_ranges=('az', 'AZ')):
    """
    Apply a caesar cipher to a string.

    The caesar cipher is a substition cipher where each letter in the given
    alphabet is replaced by a letter some fixed number down the alphabet.

    If ``shift`` is ``1``, *A* will become *B*, *B* will become *C*, etc...

    You can define the alphabets that will be shift by specifying one or more
    shift ranges. The characters will than be shifted within the given ranges.

    Args:
        shift(int): The shift to apply.
        data(str): The string to apply the cipher to.
        shift_ranges(list of str): Which alphabets to shift.

    Returns:
        str: The string with the caesar cipher applied.

    Examples:
        >>> caesar(16, 'Pwnypack')
        'Fmdofqsa'
        >>> caesar(-16, 'Fmdofqsa')
        'Pwnypack'
        >>> caesar(16, 'PWNYpack', shift_ranges=('AZ',))
        'FMDOpack'
        >>> caesar(16, 'PWNYpack', shift_ranges=('Az',))
        '`g^iFqsA'
    """
    alphabet = dict(
        (chr(c), chr((c - s + shift) % (e - s + 1) + s))
        for s, e in map(lambda r: (ord(r[0]), ord(r[-1])), shift_ranges)
        for c in range(s, e + 1)
    )
    return ''.join(alphabet.get(c, c) for c in data)