---
# System prepended metadata

title: Project 1

---

1. Teamviewer: https://download.teamviewer.com/download/TeamViewer_Setup_x64.exe
2. slack : https://downloads.slack-edge.com/releases/windows/4.31.155/prod/x64/SlackSetup.exe
3. git: https://github.com/git-for-windows/git/releases/download/v2.40.1.windows.1/Git-2.40.1-64-bit.exe
4. bit.ly/thanhvusetup
5. Vscode  
6. 
# Project 1
```python=
# -*- coding: utf-8 -*-
"""
@author: Thanh Vu and Madeline
@version: 1.19.22

Project #1: Shift and Substitution Ciphers
This project deals with an affine cipher and a jumbled-alphabet cipher
"""

# Partial Listing: Some Assembly Required

# Listing 1-1. Creating Substitution Tables

import string  # Import String library
import random # Import Random Library

# This function is used to find the greatest common demoninator of integers a and b
def fgcd(a, b):
    if(b == 0):
        return a
    else:
        return fgcd(b, a % b)

# This function creates a substitution table for an affine cipher
def create_affine_substitution(a, b):  
    # Dictionaries that will hold the plaintext and ciphertext alphabets
    encoding = {}
    decoding = {}
    
    alphabet_size = len(string.ascii_uppercase)

    
    # This loop takes every letter of the alphabet (letter), and
    # multiplies our integer value for the plaintext letter by a, and then
    # add b to the result 

    for i in range(alphabet_size):
        letter = string.ascii_uppercase[i]
        if (fgcd(a, alphabet_size) != 1):
            print("This function has no inverse, so you will not be able to decrypt it. Choose another value for a.")
            break
        elif (b not in range(alphabet_size)):
            b = b % alphabet_size
            print("b is out of range of the alphabet size. It has been modified modulo the alphabet size. The value of b is now %d" % b)
        subst_letter = string.ascii_uppercase[(a*i+b) % alphabet_size]
        encoding[letter] = subst_letter
        decoding[subst_letter] = letter     
    return encoding, decoding
   
   
# This cipher randomly shuffles the letters in the alphabet.
def create_random_cipher():
    # Empty dictionaries for the alphabets
    encoding = {}
    decoding = {}
    alphabet_size = len(string.ascii_uppercase)

    # Create shuffled alphabet.
    alphabet = list(string.ascii_uppercase)
    random.shuffle(alphabet)
    
    for i in range(alphabet_size):
        letter = string.ascii_uppercase[i]
        subst_letter = alphabet[i]
        encoding[letter] = subst_letter
        decoding[subst_letter] = letter  
    
    return encoding, decoding



#  the encode function takes a plaintext message and a
#  substitution dictionary, it returns a ciphertext message

def encode(message, subst):

    cipher = ""
    for letter in message:
        if letter.upper() in subst:
            cipher += subst[letter.upper()]
        else:
            cipher += letter.upper()
    return cipher


# The decode function takes a ciphertext message and a substitution dictionary
# It returns a plaintext message
def decode(message, subst):
    return encode(message, subst)


def printable_substitution(subst):
    # Sort by source character so things are alphabetized.
    mapping = sorted(subst.items())
    # Then create two lines: source above, target beneath.
    alphabet_line = " ".join(letter for letter, _ in mapping)
    cipher_line = " ".join(subst_letter for _, subst_letter in mapping)
    return "{}\n{}".format(alphabet_line, cipher_line)

encoding, decoding = create_affine_substitution(7,13)
#Case 1: Uppercase 
print("Case 1: Uppercase\n")
text = "ATTACK THEM"
print('Encrypted Alphabet: \n{}\n'.format(printable_substitution(encoding)))
print('Encrypted Text: {}'.format(encode(text, encoding)))
print('Decrypted Text: {}'.format(decode(encode(text, encoding), decoding)))
print("\n")


#Case 2: Mixed Case
print("\nCase 2: Mixed Case\n")
text = "Attack Them"
print('Encrypted Alphabet: \n{}\n'.format(printable_substitution(encoding)))
print('Encrypted Text: {}'.format(encode(text, encoding)))
print('Decrypted Text: {}'.format(decode(encode(text, encoding), decoding)))
print("\n")

#Case 3: a out of range
print("\nCase 3: This function has no inverse for a")
encoding, decoding = create_affine_substitution(4,13)
print("\n")

# Case 4: b out of range
print("\nCase 4: b out of range")
encoding, decoding = create_affine_substitution(3,26)
text = "Attack Them"
print('Encrypted Alphabet: \n{}\n'.format(printable_substitution(encoding)))
print('Encrypted Text: {}'.format(encode(text, encoding)))
print('Decrypted Text: {}'.format(decode(encode(text, encoding), decoding)))
print("\n")

# Case 5: a and b out of range
print("\nCase 5: Both b out of range and the function has no inverse for a")
encoding, decoding = create_affine_substitution(4,26)
print("\n")


# Test shuffled alphabet
print("\nTEST SHUFFLED ALPHABET\n")
encoding, decoding = create_random_cipher()

print("Shuffle the alphabet")
print(printable_substitution(encoding))

#Case 1: Uppercase 
print("\nCase 1: Uppercase\n")
text = "ATTACK THEM"
print('Encrypted Alphabet: \n{}\n'.format(printable_substitution(encoding)))
print('Encrypted Text: {}'.format(encode(text, encoding)))
print('Decrypted Text: {}'.format(decode(encode(text, encoding), decoding)))
print("\n")

#Case 2: Mixed Case
print("\nCase 2: Mixed Case\n")
text = "Attack Them"
print('Encrypted Alphabet: \n{}\n'.format(printable_substitution(encoding)))
print('Encrypted Text: {}'.format(encode(text, encoding)))
print('Decrypted Text: {}'.format(decode(encode(text, encoding), decoding)))
print("\n")

#Case 3: Special characters
print("\nCase 2: Special Characters\n")
text = "Atta$ck them!!"
print('Encrypted Alphabet: \n{}\n'.format(printable_substitution(encoding)))
print('Encrypted Text: {}'.format(encode(text, encoding)))
print('Decrypted Text: {}'.format(decode(encode(text, encoding), decoding)))
print("\n")
```
