hill_cipher


"/home/yossef/notes/personal/hashing/hill_cipher.md"

path: personal/hashing/hill_cipher.md

- **fileName**: hill_cipher
- **Created on**: 2025-03-20 20:54:10

starting for understand and learn the implementation for hill_cipher

how work : it's taking a key matrix and the encrption text by the matrix
key if it's must to be n*n for example n=3 so taking each time 3 char from
plain text convert to the ASCII Number (casting to number) and then multiplay
the matrix key in the 3 char [c,c,c] and then taking the modules for each
c by 26 and then convert to char again using alpabatic char

have fun ;)

## starting implementation for the hill cipher algorthim ;)

## HINT STARTING FROM MAIN TO UNDERSTAND

import numpy as np

def mod_26(n: int) -> int:
    """Returns the remainder of n % 26."""
    return n % 26


def convert_char(matrix: list[list[int]]) -> str:
    """to convert a number from list of encryption matrix to char"""
    encryption_text = ""

    # flatten the list of numPy arrays
    flat_matrix = np.concatenate(matrix)

    # convert each number to a letter (mod 26)
    for number in flat_matrix:
        x = mod_26(number)  
        x_char = chr(x + ord('a')) # convert to char again
        encryption_text += x_char

    return encryption_text

def encryption(plain_text: str, matrix) -> str:
    """
        ## for converthting  a plain_text to ascii represent
        # how it's work for example gone take the first char in word 'yosesf'
        # so it's 'y' when making ord('y') = 121 (asxii table char)
        # for more information about convert checkk this 
        # - [[../../courses/lowLevel/ascii_table.md]]
        # after that we get the numberic number of char we want a represent 
        # from 0 to 26 so subtraictoin the char with ord('a') gone get 
        # the number between the range we want
    """
    ascii_plain_text = [ord(char) - ord('a') for char in plain_text]
    # print(ascii_plain_text) # for debuger

    ## convert the arrary to np array to get the shape
    matrix = np.array(matrix)
    x = matrix.shape

    # check if the matrix is square
    if x[0] == x[1]:
        x = x[0]
    else:
        return "Error happened: not a valid dimension"

    ## encryption_data
    encryted_matrix_number = []

    # loop over the plain text in chunks of size x
    for i in range(0, len(ascii_plain_text), x):
        char_matrix = []
        for j in range(x):
            # ensure we don't go out of bounds
            if i + j < len(ascii_plain_text):
                ## print(ascii_plain_text[i + j]) # for debuger
                # append the ascii_plain_text char in array for store
                char_matrix.append(ascii_plain_text[i+j])


        ## check if there char_matrix not fulll with the right numbers
        ## of ascii_plain_text append x=>23
        # ensure the chunk size is exactly `x` by padding with 'x' (23 in ASCII)
        while len(char_matrix) < x: char_matrix.append(23)

        ## now starting convert the char_matrix that store ascii_plain_text
        ## char to np matrix to multiplay with the matrix key
        ## and append value to the final result to store
        np_char_matrix = np.array(char_matrix)
        multiply_array = matrix @ np_char_matrix
        # print(multiply_array) # for debuger

        ## append the encryped vallue from matrix in the encrypted matrix
        encryted_matrix_number.append(multiply_array)
        # print("=========================")

    # print(encryted_matrix_number) # for dubger
    return convert_char(encryted_matrix_number)




def main():

    ### testing
    plain_text = "gold"
    matrix = [[2,3],[3,6]]

    print(encryption(plain_text, matrix))

if __name__ == "__main__":
    main()

## output: cyfz

continue:./vigenere_cipher.md
before:./transposition_cipher.md