1.Write a function encipher(s, n) that takes as inputs an arbitrary string s and a non-negative integer n between 0 and 25, and that returns a new string in which the letters in s have been "rotated" by n characters forward in the alphabet, wrapping around as needed. For example:

>>> encipher('hello', 1) result: 'ifmmp'

>>> encipher('hello', 2) result: 'jgnnq'

>>> encipher('hello', 4) result: 'lipps'

Upper-case letters should be "rotated" to upper-case letters, even if you need to wrap around. For example:

>>> encipher('XYZ', 3)

result: 'ABC'

Lower-case letters should be "rotated" to lower-case letters:

>>> encipher('xyz', 3)

result: 'abc'

Non-alphabetic characters should be left unchanged:

>>> encipher('#caesar!', 2)

result: '#ecguct!'

**Hints/reminders:**

You can use the built-in functions ord and chr convert from single-character strings to integers and back:

>>> ord('a')

result: 97

>>> chr(97)

result: 'a'

You can use the following test to determine if a character is between 'a' and 'z' in the alphabet:

if 'a' <= c <= 'z':

A similar test will work for upper-case letters.

We recommend writing a helper function rot(c, n) that rotates a single character c forward by n spots in the alphabet. We have given you a template for this helper function in ps3pr3.py that checks to ensure that c is a single-character string. We wrote rot13(c) in lecture; rot(c, n) will be very close to rot13(c)! You can test your rot(c, n) as follows:

>>> rot('a', 1)

result: 'b'

>>> rot('y', 2)

result: 'a'

>>> rot('A', 3)

result: 'D'

>>> rot('Y', 3)

result: 'B'

>>> rot('!', 4)

result: '!'

Once you have rot(c, n), you can write a recursive encipher function.

Once you think you have everything working, here are three more examples to try:

>>> encipher('xyza', 1)

result: 'yzab'

>>> encipher('Z A', 2)

result: 'B C'

>>> encipher('Caesar cipher? I prefer Caesar salad.', 25)

result: 'Bzdrzq bhogdq? H oqdedq Bzdrzq rzkzc.'

2.Write a function decipher(s) that takes as input an arbitrary string s that has already been enciphered by having its characters "rotated" by some amount (possibly 0). decipher should return, to the best of its ability, the original English string, which will be some rotation (possibly 0) of the input string s. For example:

>>> decipher('Bzdrzq bhogdq? H oqdedq Bzdrzq rzkzc.')

result: 'Caesar cipher? I prefer Caesar salad.'

Here are two more examples:

>>> decipher('Hu lkbjhapvu pz doha ylthpuz hmaly dl mvynla lclyfaopun dl ohcl slhyulk.')

result: 'An education is what remains after we forget everything we have learned.'

>>> decipher('python')

result: 'eniwdc'

------------------------------------

def rot(c, n):

""" your docstring goes here """

# check to ensure that c is a single character

assert(type(c) == str and len(c) == 1)

def letter_prob(c):

""" if c is the space character (' ') or an alphabetic character,

returns c's monogram probability (for English);

returns 1.0 for any other character.

adapted from:

"""

# check to ensure that c is a single character

assert(type(c) == str and len(c) == 1)

if c == ' ': return 0.1904

if c == 'e' or c == 'E': return 0.1017

if c == 't' or c == 'T': return 0.0737

if c == 'a' or c == 'A': return 0.0661

if c == 'o' or c == 'O': return 0.0610

if c == 'i' or c == 'I': return 0.0562

if c == 'n' or c == 'N': return 0.0557

if c == 'h' or c == 'H': return 0.0542

if c == 's' or c == 'S': return 0.0508

if c == 'r' or c == 'R': return 0.0458

if c == 'd' or c == 'D': return 0.0369

if c == 'l' or c == 'L': return 0.0325

if c == 'u' or c == 'U': return 0.0228

if c == 'm' or c == 'M': return 0.0205

if c == 'c' or c == 'C': return 0.0192

if c == 'w' or c == 'W': return 0.0190

if c == 'f' or c == 'F': return 0.0175

if c == 'y' or c == 'Y': return 0.0165

if c == 'g' or c == 'G': return 0.0161

if c == 'p' or c == 'P': return 0.0131

if c == 'b' or c == 'B': return 0.0115

if c == 'v' or c == 'V': return 0.0088

if c == 'k' or c == 'K': return 0.0066

if c == 'x' or c == 'X': return 0.0014

if c == 'j' or c == 'J': return 0.0008

if c == 'q' or c == 'Q': return 0.0008

if c == 'z' or c == 'Z': return 0.0005

return 1.0

1.Write a function encipher(s, n) that takes as inputs an arbitrary string s and a non-negative integer n between 0 and 25, and that returns a new string in which the letters in s have been "rotated" by n characters forward in the alphabet, wrapping around as needed. For example:

>>> encipher('hello', 1) result: 'ifmmp'

>>> encipher('hello', 2) result: 'jgnnq'

>>> encipher('hello', 4) result: 'lipps'

Upper-case letters should be "rotated" to upper-case letters, even if you need to wrap around. For example:

>>> encipher('XYZ', 3)

result: 'ABC'

Lower-case letters should be "rotated" to lower-case letters:

>>> encipher('xyz', 3)

result: 'abc'

Non-alphabetic characters should be left unchanged:

>>> encipher('#caesar!', 2)

result: '#ecguct!'

**Hints/reminders:**

You can use the built-in functions ord and chr convert from single-character strings to integers and back:

>>> ord('a')

result: 97

>>> chr(97)

result: 'a'

You can use the following test to determine if a character is between 'a' and 'z' in the alphabet:

if 'a' <= c <= 'z':

A similar test will work for upper-case letters.

We recommend writing a helper function rot(c, n) that rotates a single character c forward by n spots in the alphabet. We have given you a template for this helper function in ps3pr3.py that checks to ensure that c is a single-character string. We wrote rot13(c) in lecture; rot(c, n) will be very close to rot13(c)! You can test your rot(c, n) as follows:

>>> rot('a', 1)

result: 'b'

>>> rot('y', 2)

result: 'a'

>>> rot('A', 3)

result: 'D'

>>> rot('Y', 3)

result: 'B'

>>> rot('!', 4)

result: '!'

Once you have rot(c, n), you can write a recursive encipher function.

Once you think you have everything working, here are three more examples to try:

>>> encipher('xyza', 1)

result: 'yzab'

>>> encipher('Z A', 2)

result: 'B C'

>>> encipher('Caesar cipher? I prefer Caesar salad.', 25)

result: 'Bzdrzq bhogdq? H oqdedq Bzdrzq rzkzc.'

2.Write a function decipher(s) that takes as input an arbitrary string s that has already been enciphered by having its characters "rotated" by some amount (possibly 0). decipher should return, to the best of its ability, the original English string, which will be some rotation (possibly 0) of the input string s. For example:

>>> decipher('Bzdrzq bhogdq? H oqdedq Bzdrzq rzkzc.')

result: 'Caesar cipher? I prefer Caesar salad.'

Here are two more examples:

>>> decipher('Hu lkbjhapvu pz doha ylthpuz hmaly dl mvynla lclyfaopun dl ohcl slhyulk.')

result: 'An education is what remains after we forget everything we have learned.'

>>> decipher('python')

result: 'eniwdc'

------------------------------------

def rot(c, n):

""" your docstring goes here """

# check to ensure that c is a single character

assert(type(c) == str and len(c) == 1)

def letter_prob(c):

""" if c is the space character (' ') or an alphabetic character,

returns c's monogram probability (for English);

returns 1.0 for any other character.

adapted from:

"""

# check to ensure that c is a single character

assert(type(c) == str and len(c) == 1)

if c == ' ': return 0.1904

if c == 'e' or c == 'E': return 0.1017

if c == 't' or c == 'T': return 0.0737

if c == 'a' or c == 'A': return 0.0661

if c == 'o' or c == 'O': return 0.0610

if c == 'i' or c == 'I': return 0.0562

if c == 'n' or c == 'N': return 0.0557

if c == 'h' or c == 'H': return 0.0542

if c == 's' or c == 'S': return 0.0508

if c == 'r' or c == 'R': return 0.0458

if c == 'd' or c == 'D': return 0.0369

if c == 'l' or c == 'L': return 0.0325

if c == 'u' or c == 'U': return 0.0228

if c == 'm' or c == 'M': return 0.0205

if c == 'c' or c == 'C': return 0.0192

if c == 'w' or c == 'W': return 0.0190

if c == 'f' or c == 'F': return 0.0175

if c == 'y' or c == 'Y': return 0.0165

if c == 'g' or c == 'G': return 0.0161

if c == 'p' or c == 'P': return 0.0131

if c == 'b' or c == 'B': return 0.0115

if c == 'v' or c == 'V': return 0.0088

if c == 'k' or c == 'K': return 0.0066

if c == 'x' or c == 'X': return 0.0014

if c == 'j' or c == 'J': return 0.0008

if c == 'q' or c == 'Q': return 0.0008

if c == 'z' or c == 'Z': return 0.0005

return 1.0