# Encrypt a message using the Polybius cipher and a given keyword

# Create a matrix, Polybius, containing character data from assignment

# First create an empty matrix of size 4x10 in which to assign character data
Polybius <- matrix(nrow = 4, ncol = 10)

# Create the numerical labels for rows and columns in matrix
rownames(Polybius) <- 0:3
colnames(Polybius) <- 0:9

# Fill the matrix with provided cipher data
# Fill row 0
Polybius["0", c("1", "2", "3")] <- c("Z", "M", "D")
Polybius["0", c("5", "6", "7")] <- c("K", "W", "Q")
Polybius["0", "9"] <- "G"

# Fill row 1
Polybius["1", "0"] <- "V"
Polybius["1", "2"] <- "A"
Polybius["1", "4"] <- "R"
Polybius["1", "6"] <- "E"
Polybius["1", c("8", "9")] <- c("T", "O")

# Fill row 2
Polybius["2", c("1", "2", "3", "4", "5", "6")] <- c("J", "I", "S", "B", "P", "Y")
Polybius["2", "8"] <- "F"

# Fill row 3
Polybius["3", "0"] <- "L"
Polybius["3", "3"] <- "C"
Polybius["3", c("5", "6", "7")] <- c("U", "H", "N")
Polybius["3", "9"] <- "X"

# Display the matrix
Polybius


# ENCRYPTION
# Encrypt a message using the Polybius matrix and a given keyword

# Define the text to be encrypted and the keyword
plainText <- "RAISETHEFLAG"
keyword <- "PEACH"
keyText <- c(keyword, plainText)

# Convert the text and the keyword into a vector of individual characters
# Create an empty vector in which the individual characters of plainText and
# keyword will be added
dividedText <- NULL

# Convert plainText and keyword into a vector of individual characters using 
# loops
for (i in keyText) {
  for (j in 1:nchar(i)) {
    individualChar <- substring(i, j, j)
    dividedText <- c(dividedText, individualChar)
  }
}

# Display dividedText
dividedText

# Create an empty vector in which the encrypted keyword and message will 
# be added
cipherText <- NULL

# Convert each character of keyword and message to encrypted text
for (i in dividedText) {
    
    # Find coordinates of character in matrix Polybius
    charPosition <- which(Polybius==i, arr.ind=T)
    
    # Extract the row and column index into variables
    rowIndex <- charPosition[1]
    colIndex <- charPosition[2]
    
    # Use row and column indexes to extract row and column names
    rowName <- rownames(Polybius)[rowIndex]
    colName <- colnames(Polybius)[colIndex]
    
    # Combine the row and column index into one string without spaces
    encryptedChar <- paste(rowName, colName, sep="")
    
    # Add encrypted character to cipherText
    cipherText <- c(cipherText, encryptedChar)
}

# Separate the encrypted keyword and message into their own vectors
encryptedKeyword <- cipherText[1:nchar(keyword)]
encryptedMessage <- cipherText[(nchar(keyword)+1):length(cipherText)] 

# Adjust the values in the encrypted message using the keyword
# Turn the vectors into numerical vectors to enable vector addition
encryptedNumKeyword <- as.numeric(encryptedKeyword)
encryptedNumMessage <- as.numeric(encryptedMessage)

# Add the values of the encrypted keyword to those of the encrypted message
# knowing that R will recycle encryptedNumKeyword to make it equal in length
# to encryptedNumMessage
adjustedMessage <- encryptedNumKeyword + encryptedNumMessage

# Turn adjustedMessage into a vector of characters
adjustedMessage <- as.character(adjustedMessage)

# Check if each element has two digits and put a zero in front if it doesn't
newAdjustedString <- NULL

for (i in adjustedMessage) {
  
  if (nchar(i) != 2) {
    
    # Add a zero in front of number
    newStringValue <- paste("0", i, sep="")
    
  } else {
    newStringValue <- i
  }
  
  newAdjustedString <- c(newAdjustedString, newStringValue)
}

# Recombine message and keyword
newAdjustedString <- c(encryptedKeyword, newAdjustedString)

# Turn into a vector with one element
noSpaceAdjusted <- NULL

for (i in newAdjustedString) {
  noSpaceAdjusted <- paste(noSpaceAdjusted, i, sep="")
}

# Check whether length of string is a multiple of 5 using remainder
remainder <- nchar(noSpaceAdjusted)%%5

# Add values to noSpaceAdjusted to make it divisible by five
for (i in 1:(5-remainder)) {
  noSpaceAdjusted <- paste(noSpaceAdjusted, sample(0:9, 1), sep="")
}

# Split numbers into groups of five within the string

# Create an empty vector in which the groups of five numbers will be added
finalText <- NULL

# Loop over the first element plus every fifth after that
for (i in seq(1, nchar(noSpaceAdjusted)-4, 5)) {
  
  # Add that element plus the next four to groupedNumbers
  groupedNumbers <- substring(noSpaceAdjusted, i, i+4)

  # Add the grouped digit elements to finalText, separated by spaces 
  finalText <- paste(finalText, groupedNumbers, sep=" ")
}

# Remove the space before the first group of digits in finalText
finalText <- sub(" ", "", finalText)

# Display the encrypted text
finalText




# DECRYPTION
# Decrypt a message using the Polybius matrix and a given keyword

cipherText <- paste("48521 82559 53320 83151 44382 13349 ",
              "40352 42260 48521 84349 48343 23518", sep="")
keyword <- "LEMON"

# Remove the spaces from the single element vector, cipherText
joinedCipherText <- gsub(" ", "", cipherText)

# Convert joinedCipherText into a vector of multiple elements, each
# containing two digits
# Check whether the number of digits in joinedCipherText is divisible by two
# with no remainder, and remove the last element if not
if (nchar(joinedCipherText)%%2 != 0) {
  joinedCipherText <- substring(joinedCipherText, 1, nchar(joinedCipherText)-1)
}

# Create an empty vector to which the elements containing two digits will 
# be added
twosCipherText <- NULL

# Loop over the first element plus every second element after that
for (i in seq(1, nchar(joinedCipherText)-1, 2)) {
  
  # Add that element plus the next one to twoDigitElement
  twoDigitElement <- substring(joinedCipherText, i, i+1)
  
  # Add the two digit elements to twosCipherText
  twosCipherText <- c(twosCipherText, twoDigitElement)
}

# Adjust the values in the encrypted message using the keyword 

# Encrypt the keyword
# Convert the keyword into a vector of individual characters
# Create an empty vector in which individual characters of keyword will
# be added
dividedKey <- NULL

# Convert the keyword into a vector of individual characters using a loop
for (i in 1:nchar(keyword)) {
  individualChar <- substring(keyword, i, i)
  dividedKey <- c(dividedKey, individualChar)
}

# Turn the divided keyword into encrypted text
# Create an empty vector in which the encrypted keyword will be added
cipherKey <- NULL

# Convert each character of message to encrypted text
for (i in dividedKey) {
  
  # Find coordinates of character in Polybius matrix
  charPosition <- which(Polybius==i, arr.ind=T)
  
  # Extract the row and column index into variables
  rowIndex <- charPosition[1]
  colIndex <- charPosition[2]
  
  # Use row and column indexes to extract row and column names
  rowName <- rownames(Polybius)[rowIndex]
  colName <- colnames(Polybius)[colIndex]
  
  # Combine the row and column index into one string without spaces
  encryptedChar <- paste(rowName, colName, sep="")
  
  # Add encrypted character to cipherText
  cipherKey <- c(cipherKey, encryptedChar)
}

# Display the encrypted keyword
cipherKey

# Turn the vectors into numerical vectors to enable vector subtraction
encryptedNumKeyword <- as.numeric(cipherKey)
encryptedNumMessage <- as.numeric(twosCipherText)

# Subtract the values of the encrypted keyword from those of the encrypted 
# message knowing that R will recycle encryptedNumKeyword to make it equal in 
# length to encryptedNumMessage
adjustedMessage <- encryptedNumMessage - encryptedNumKeyword

# Remove negatives (which will probably exist at end if they're there)
adjustedMessage <- adjustedMessage[adjustedMessage > 0]

# Turn adjustedMessage back into a string
adjustedMessageString <- as.character(adjustedMessage)

# Check if each element has two digits and put a zero in front if it doesn't
newAdjustedString <- NULL

for (i in adjustedMessageString) {
  
  if (nchar(i) != 2) {
    
    # Add zero in front of number
    newStringValue <- paste("0", i, sep="")
    
  } else {
    newStringValue <- i
  }
  
  newAdjustedString <- c(newAdjustedString, newStringValue)
}

# Create an empty vector to which the decrypted letters will be added
plainText <- NULL

# For each element, translate digits into coordinates
for (i in newAdjustedString) {
  
  # Save the row and column index as variables
  rowValue <- substring(i, 1, 1)
  colValue <- substring(i, 2, 2)
  
  # Find the corresponding character using the matrix coordinates
  decryptedLetter <- Polybius[rowValue, colValue]
  
  # Check if any coordinates correspond to NA i.e. from random digits added
  # to the end of the last group of five, and ignore these
  if (decryptedLetter != 'NA') {
    plainText <- paste(plainText, decryptedLetter, sep="")
  }
}

# Display the decrypted message
plainText
