Problem 1.1: Encryption Methods (100pts)

There are three main subclasses for encryption, each implementing a different technique:

  • Shift Cipher: The constructor takes an integer representing the shift amount. The encrypt method shifts each letter of the string by the given number of positions in the alphabet.
  • Dictionary Cipher: The constructor takes a dictionary that maps characters to other characters. The encrypt method replaces each letter in the string with its corresponding character from the dictionary.
  • Fence Cipher: The constructor takes an integer representing the number of rails. The encrypt method uses the rail fence cipher to rearrange the string based on the number of rails.

Hint 1: You might find some built-in functions that convert between single character strings and unicode (a character encoding that is more general than ascii code) integers helpful, like ord, chr, etc.

Hint 2: You might find some built-in string methods helpful, like join, etc.

Hint 3: How the Fence Cipher Works:

  1. Set the Number of Rails (n): Choose an integer n that represents the number of rails (or rows).
  2. Fill the Rails: Write the characters of the plaintext column by column.
  3. Read the Rails: Read the characters row by row to get the ciphertext.

Let's walk through an example where the plaintext is "HELLO" and we choose n = 3 (3 rails):

Plaintext: h e l l o
Rails:     3

Step 1: Fill the Rails:
Rail 1: h l
Rail 2: e o
Rail 3: l

Step 2: Read the characters row by row:
Ciphertext: hleol

Both subclasses of the encryption methods have an encrypt method that takes a string and returns the encrypted version of the string.

class methods:
    """The base class for encryption methods."""

    def encrypt(self, message):
        """Encrypt the message."""
        pass


class shiftcipher(methods):
    """A class for shift cipher encryption method.
    >>> cipher = shiftcipher(3)
    >>> cipher.encrypt('hello')
    'khoor'
    >>> cipher.encrypt('world')
    'zruog'
    """

    def __init__(self, shift):
        """
        Initialize the shift cipher with a shift.
        If shift is greater than 26, shift = shift % 26.
        """
        "*** YOUR CODE HERE ***"

    def encrypt(self, message):
        """Encrypt the message by shifting each character by the shift."""
        "*** YOUR CODE HERE ***"


class dictionarycipher(methods):
    """A class for dictionary cipher encryption method.
    >>> cipher = dictionarycipher({'h': 'a', 'e': 'b', 'l': 'c', 'o': 'd', 'w': 'e', 'r': 'f', 'd': 'g'})
    >>> cipher.encrypt('hello')
    'abccd'
    >>> cipher.encrypt('world')
    'edfcg'
    """

    def __init__(self, dictionary):
        """
        Initialize the dictionary cipher with a dictionary.
        If a key is not in the dictionary, the value is the key itself.
        We promise that any letter will only appear once as a value in the dictionary.
        """
        "*** YOUR CODE HERE ***"

    def encrypt(self, message):
        """Encrypt the message using the dictionary cipher."""
        "*** YOUR CODE HERE ***"


class fencecipher(methods):
    """A class for fence cipher encryption method.
    >>> cipher = fencecipher(3)
    >>> cipher.encrypt('hello')
    'hleol'
    >>> cipher.encrypt('world')
    'wlodr'
    """

    def __init__(self, rails):
        """Initialize the fence cipher with a number of rails."""
        "*** YOUR CODE HERE ***"

    def encrypt(self, message):
        """Encrypt the message using the fence cipher."""
        "*** YOUR CODE HERE ***"