import time
import gc
import os 
time.sleep(0.1) # Wait for USB to become ready


def split_binary_into_chunks(binary_string,chunk_size):
    return [binary_string[i:i+chunk_size] for i in range(0, len(binary_string), chunk_size)]

def merge_chunks_into_binary(chunk_list):
    return ''.join(chunk_list)

def xor_binary_strings(binary_str1, binary_str2):
    # Perform XOR operation on binary strings
    result = ''.join(str(int(bit1) ^ int(bit2)) for bit1, bit2 in zip(binary_str1, binary_str2))
    return result


#Encryption functions

def encrypt_string_to_decimal(input_str):
    decimal_values_concatenated = 0  # Initialize as integer
    for char in input_str:
        decimal_values_concatenated = decimal_values_concatenated * 1000 + ord(char)  # Convert character to ASCII value and concatenate
    return decimal_values_concatenated

def decimal_to_multiple_128bit_binary(decimal_number):
    # Convert decimal number to binary
    binary_number = bin(decimal_number)[2:]

    # Calculate the number of bits needed to make it a multiple of 128
    padding_length = (128 - len(binary_number) % 128) % 128

    # Add padding zeros to make it a multiple of 128 bits
    padded_binary_number = '0' * padding_length + binary_number

    return padded_binary_number

def NOT_gate_32_bit(input_bits):
    # Ensure the input is exactly 32 bits long
    if len(input_bits) != 32:
        raise ValueError("Input must be 32 bits long")

    # Perform the NOT operation on each bit of the input
    output_bits = ''.join(['1' if bit == '0' else '0' for bit in input_bits])

    return output_bits

def split_and_swap_32_to_64(input_block):
    # Ensure the input block is exactly 32 bits
    if len(input_block) != 32:
        raise ValueError("Input block must be 32 bits long")

    # Split the input block into left and right halves (16 bits each)
    left_half = input_block[:16]
    right_half = input_block[16:]

    #Generate the middle part of 32 bits compliment using function
    middle_element=NOT_gate_32_bit(input_block)
    element_0=right_half+middle_element+left_half
    return element_0


def rotate_64bit(input_str):
    # Step 1: Create an 8x8 matrix from the input binary string
    matrix = []
    for i in range(8):
        row = input_str[i*8:(i+1)*8]
        matrix.append([int(bit) for bit in row])

    # Step 2: Rotate the matrix 90 degrees clockwise
    rotated_matrix = list(zip(*matrix[::-1]))

    # Step 3: Convert the rotated matrix back to a binary string
    rotated_binary_str = ''.join(''.join(map(str, row)) for row in rotated_matrix)

    return rotated_binary_str



def encryption(input_text, k1):
    a= decimal_to_multiple_128bit_binary(encrypt_string_to_decimal(input_text))
    for i in range(32):
        to_return=split_binary_into_chunks(a,32)
        arr=[]
        for i in to_return:
            temp=split_and_swap_32_to_64(i)
            temp=rotate_64bit(temp)
            temp= xor_binary_strings(temp,k1)
            arr.append(temp)
        to_return=merge_chunks_into_binary(arr)
    return to_return



plain_text=input("Enter the text to encrypt: ")
key_1='1010000001101010101000000010101010000000010010101001010101100010'
# print(len(key_1))
start_time=time.time_ns()
gc.collect()
initial_free = gc.mem_free()


Encrypted_text=encryption(plain_text, key_1)

end_time = time.time_ns()
gc.collect()
final_free = gc.mem_free()

execution_time=end_time-start_time
print("Execution time: ", execution_time)

ram_used = initial_free - final_free
print("RAM used by the program:", ram_used, " bytes.")



print("Original text is:    ",plain_text)

print("Original text is of ",len(plain_text)," bits")
BOOTSELLED1239USBRaspberryPiPico©2020RP2-8020/21P64M15.00TTT