# Define the Polybius matrix
# Reference: Chapter 3, Matrices and Arrays (p. 59-63), Matrix Creation
# Create a matrix that maps letters to rows and columns for encryption and decryption
polybius <- matrix(c(" ", "Z", "M", "D", " ", "K", "W", "Q", " ", "G", 
                     "V", " ", "A", " ", "R", " ", "E", " ", "T", "O", 
                     " ", "J", "I", "S", "B", "P", "Y", " ", "F", " ", 
                     "L", " ", " ", "C", " ", "U", "H", "N", " ", "X"), 
                   nrow = 4, byrow = TRUE)    # Fill the matrix row by row

rownames(polybius) <- 0:3  # Assign row numbers (0-based indexing for decryption logic)
colnames(polybius) <- 0:9  # Assign column numbers (0-based indexing)

# -------------------------------------
# Part 2: Encrypt the given message with the keyword "APPLE"
# -------------------------------------
# Define the message and keyword

message <- "RAISETHEFLAG"  # The plaintext message to encrypt
keyword <- "APPLE"         # The keyword used for encryption

# Split message and keyword into individual letters
# Reference: Chapter 11, String Manipulation (p. 251)

message_chars <- sapply(seq_len(nchar(message)), function(i) substring(message, i, i))  # Split message into characters
keyword_chars <- sapply(seq_len(nchar(keyword)), function(i) substring(keyword, i, i))  # Split keyword into characters

# Get numeric coordinates for the message from the Polybius matrix
# Reference: Chapter 3.2.2, Matrix Indexing (p. 62)

message_coords <- sapply(message_chars, function(char)  # Loop through each character in the message
{
  for (row in 0:3)  # Iterate over rows of the Polybius matrix
  {
    for (col in 0:9)  # Iterate over columns of the Polybius matrix
    {
      if (polybius[row + 1, col + 1] == char)  # Check if the character matches in the matrix
      {
        return(row * 10 + col)  # Return numeric coordinate as row * 10 + col
      }
    }
  }
})

# Repeat the keyword to match the length of the message
# Reference: Chapter 2, Recycling in Vectors (p. 29)

keyword_coords <- sapply(rep(keyword_chars,length.out = length(message_chars)), function(char)  # Loop through repeated keyword characters
{
  for (row in 0:3)  # Iterate over rows of the Polybius matrix
  {
    for (col in 0:9)  # Iterate over columns of the Polybius matrix
    {
      if (polybius[row + 1, col + 1] == char)  # Check if the character matches in the matrix
      {
        return(row * 10 + col)  # Return numeric coordinate as row * 10 + col
      }
    }
  }
})

# Add message and keyword coordinates to encrypt
# Reference: Chapter 8, Math and Simulations (p. 189-190), Modular Arithmetic

ciphertext_coords <- (message_coords + keyword_coords)  # Add message and keyword coordinates for encryption

# Convert numeric coordinates to a single ciphertext string
# Reference: Chapter 11.1.3, String Concatenation with paste() (p. 252)

ciphertext_string <- paste(ciphertext_coords, collapse = "")  # Combine all numeric coordinates into a single string

# Divide the ciphertext into groups of 5 digits
# Reference: Chapter 11.1.5, substr() Function (p. 253)

finaltext <- c()  # Initialize vector for storing ciphertext groups

for (i in seq(1, nchar(ciphertext_string), by = 5))  # Loop through ciphertext in steps of 5
{
  group <- substring(ciphertext_string, i, min(i + 4, nchar(ciphertext_string)))  # Extract a group of up to 5 digits
  
  # Add random digits to the last group if less than 5 digits
  if (nchar(group) < 5)  # Check if the group is shorter than 5 digits
  {
    group <- paste0(group, paste(sample(0:9, 5 - nchar(group), replace = TRUE), collapse = ""))  # Add random digits
  }
  
  finaltext <- c(finaltext, group)  # Append the group to the final ciphertext vector
}

# Display the final ciphertext in groups
finaltext  # Print the ciphertext groups


# -------------------------------------
# Part 3: Decrypt the ciphertext using the keyword "LEMON"
# -------------------------------------

# Input ciphertext and keyword
# Reference: Chapter 11, String Manipulation (p. 251-253), Combining Strings

ciphertext <- "48521 82559 53320 83151 44382 13349 40352 42260 48521 84349 48343 23518"
keyword1 <- "LEMON"

# Step 1: Split ciphertext into pairs
# Reference: Chapter 11.1, substr() Function (p. 253), Removing Spaces and Processing Substrings
# Ciphertext is cleaned (spaces removed) and split into 2-digit numeric pairs

ciphertext_clean <- paste(strsplit(ciphertext, " ")[[1]], collapse = "") # Remove spaces

pairs <- c()  # Initialize vector for storing numeric pairs

for (i in seq(1, nchar(ciphertext_clean), by = 2)) # Loop through ciphertext in steps of 2
{ 
  p <- as.numeric(substr(ciphertext_clean, i, i + 1)) # Extract 2-digit pair
  pairs <- c(pairs, p)  # Append the numeric pair to the vector
}

# Step 2: Convert keyword to numeric values
# Reference: Chapter 5, Matrix Indexing (p. 62), Accessing Matrix Elements
# Each letter in the keyword is mapped to a numeric value using the Polybius matrix

k_num <- c()  # Initialize vector for numeric values of keyword

for (char in strsplit(keyword1, "")[[1]]) # Split the keyword into individual letters
{ 
  for (r in 0:3) # Loop through rows of the Polybius matrix
  {  
    for (c in 0:9) 
    {  # Loop through columns of the Polybius matrix
      if (polybius[r + 1, c + 1] == char) # Match letter in matrix (adjusting for 1-based indexing)
      { 
        k_num <- c(k_num, r * 10 + c) # Compute numeric value as row * 10 + column
      }
    }
  }
}

# Step 3: Decrypt the ciphertext pairs
# Reference: Chapter 8, Modular Arithmetic (p. 189-190)
# Subtract the keyword numeric value from each ciphertext pair, wrap negatives, and map back to the Polybius matrix

plaintext <- ""  # Initialize an empty string for the final plaintext

k_len <- length(k_num)  # Length of the numeric keyword sequence

for (j in 1:length(pairs)) # Loop through all ciphertext pairs
{ 
  c_val <- pairs[j]  # Current ciphertext pair value
  k_val <- k_num[(j - 1) %% k_len + 1] # Cycle through keyword values based on current position
  
  # Adjust cipher value and handle negative wrapping
  adj_val <- c_val - k_val  # Subtract the numeric value of the keyword
  if (adj_val < 0)  # If the adjusted value is negative
  { 
    adj_val <- adj_val + 40 # Wrap into valid range (0-39)
  }
  
  # Extract row and column from the adjusted value
  r <- adj_val %/% 10  # Integer division to get the row
  c <- adj_val %% 10  # Modulo operation to get the column
  
  # Fetch the corresponding letter from the Polybius matrix
  letter <- polybius[r + 1, c + 1]  # Adjust for R's 1-based indexing
  if (letter != " ") 
  {  # Ignore spaces in the matrix
    plaintext <- paste0(plaintext, letter) 
  }
}

# Final Output
# Display the final decrypted plaintext
plaintext  
