/**
 *--------------------------------------------------------------------\n
 *          HSLU T&A Hochschule Luzern Technik+Architektur            \n
 *--------------------------------------------------------------------\n
 *
 * \brief         n size number computation - ASYD
 * \file
 * \author        Stefano Nicora, stefano.nicora@hslu.ch
 * \date          17.05.2022
 *
 *--------------------------------------------------------------------
 */

#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>	/* calloc, malloc, free, etc. */
#include <stdbool.h>
#include <string.h>
#include "encryptionArithmetic.h"

 /* link two numbers via a mathematical addition */
t_encryptionArithmetic* encryptionArithmetic_ADD(uint32_t* number1, uint32_t* number2, t_encryptionArithmetic* result, uint16_t size);

/* link two numbers via a mathematical subtraction */
t_encryptionArithmetic* encryptionArithmetic_SUB(uint32_t* number1, uint32_t* number2, t_encryptionArithmetic* result, uint16_t size);

/* link two numbers via a mathematical division */
t_encryptionArithmetic* encryptionArithmetic_DIV(uint32_t* number1, uint32_t* number2, t_encryptionArithmetic* result, uint16_t size);

/* link two numbers via a mathematical multiplication */
t_encryptionArithmetic* encryptionArithmetic_MUL(uint32_t* number1, uint32_t* number2, t_encryptionArithmetic* result, uint16_t size);

/* link two numbers via logical AND */
t_encryptionArithmetic* encryptionArithmetic_AND(uint32_t* number1, uint32_t* number2, t_encryptionArithmetic* result, uint16_t size);

/* link two numbers via logical OR */
t_encryptionArithmetic* encryptionArithmetic_OR(uint32_t* number1, uint32_t* number2, t_encryptionArithmetic* result, uint16_t size);

/* link two numbers via logical XOR */
t_encryptionArithmetic* encryptionArithmetic_XOR(uint32_t* number1, uint32_t* number2, t_encryptionArithmetic* result, uint16_t size);

/* shift number by amount to the left */
void encryptionArithmetic_LSL(uint32_t* number, uint8_t amount, uint16_t size);

/* empty an existing number of its content */
void encryptionArithmetic_clearNumber(uint32_t* number, uint16_t size);

/**
  * Computes the computed result of two numbers while specifying the operation with an OPCODE
  *
  * Usage:
  * Arrays with uint32_t sized entries (number1 and number2) hold the wanted values which get computed bitwise
  * from LSB to MSB starting at index 0. Data gets stored as "little Endian".
  * array[0] = [X X X X X X X X X]
  * 	  		MSB			    LSB
  *
  * Example:
  * uint32_t Number1[10] = { Number };
  * uint32_t Number2[10] = { Number };
  * t_encryptionArithmetic *result, number;
  * result = &number;
  * encryptionArithmetic_Init(result, size);
  * encryptionArithmetic(Number1, Number2, result, size, ADD);
  * encryptionArithmetic_DeInit(result->number);
  *
  * @param [in] number1
  *   pointer to the memory location of the first number (stored in 32bit-chunks)
  * @param [in] number2
  *   pointer to the memory location of the second number (stored in 32bit-chunks)
  * @param [in] result
  *   pointer to the memory location of the address which holds the computed number afterwards
  * @param [in] size
  *   Size is in bits
  * @param [in] OPCODE
  *   defines the desired operation that gets computed
  *   Available operand: ADD, SUB, DIV, MUL, AND, OR, XOR
  * @return
  *   pointer to the memory address of the struct
  */

/* Important: The library takes massively advantage of pointers and memory allocation
 * Even though there has been done a lot of testing in regard to memory & buffer overflows
 * they might still happen if you aren't careful. You should rather allocate too much memory than
 * too little and risk the corruption of data outside your desired working area.
 */

//03.02: deinit-counter added
uint16_t deinitCounter = 0;

t_encryptionArithmetic* encryptionArithmetic(uint32_t* number1, uint32_t* number2, t_encryptionArithmetic* result, uint16_t size, t_operation OPCODE)
{
	switch (OPCODE)
	{
	case ADD: 	return encryptionArithmetic_ADD(&(*number1), &(*number2), &(*result), size); break;
	case SUB:	return encryptionArithmetic_SUB(&(*number1), &(*number2), &(*result), size); break;
	case DIV:	return encryptionArithmetic_DIV(&(*number1), &(*number2), &(*result), size); break;
	case MUL:	return encryptionArithmetic_MUL(&(*number1), &(*number2), &(*result), size); break;
	case AND: 	return encryptionArithmetic_AND(&(*number1), &(*number2), &(*result), size); break;
	case OR: 	return encryptionArithmetic_OR(&(*number1), &(*number2), &(*result), size); break;
	case XOR: 	return encryptionArithmetic_XOR(&(*number1), &(*number2), &(*result), size); break;
	default: 	printf("OPCODE not recognized. Please select an available one\n"); return 0; break;
	}
}

t_encryptionArithmetic* encryptionArithmetic_AND(uint32_t* number1, uint32_t* number2, t_encryptionArithmetic* result, uint16_t size)
{
	uint8_t imm = 0;
	uint32_t lsl1;
	uint32_t lsl2;
	uint32_t lsr1;
	uint32_t lsr2;
	for (uint16_t j = 0; j < size / 32; j++)
	{
		for (uint8_t i = 0; i < 32; i++)
		{
			lsl1 = (*(number1 + imm)) << (31 - i);
			lsl2 = (*(number2 + imm)) << (31 - i);
			lsr1 = lsl1 >> (31);
			lsr2 = lsl2 >> (31);
			if (lsr1 & lsr2)
			{
				*(result->number + imm) |= 1 << i;
			}
		}
		imm++;
	}
	result->compSuccess = true;
	return result;
}

t_encryptionArithmetic* encryptionArithmetic_XOR(uint32_t* number1, uint32_t* number2, t_encryptionArithmetic* result, uint16_t size)
{
	uint8_t imm = 0;
	uint32_t lsl1;
	uint32_t lsl2;
	uint32_t lsr1;
	uint32_t lsr2;
	for (uint16_t j = 0; j < size / 32; j++)
	{
		for (uint8_t i = 0; i < 32; i++)
		{
			lsl1 = (*(number1 + imm)) << (31 - i);
			lsl2 = (*(number2 + imm)) << (31 - i);
			lsr1 = lsl1 >> (31);
			lsr2 = lsl2 >> (31);
			if (lsr1 ^ lsr2)
			{
				*(result->number + imm) |= 1 << i;
			}
		}
		imm++;
	}
	result->compSuccess = true;
	return result;
}

t_encryptionArithmetic* encryptionArithmetic_OR(uint32_t* number1, uint32_t* number2, t_encryptionArithmetic* result, uint16_t size)
{
	uint8_t imm = 0;
	uint32_t lsl1;
	uint32_t lsl2;
	uint32_t lsr1;
	uint32_t lsr2;
	for (uint16_t j = 0; j < size / 32; j++)
	{
		for (uint8_t i = 0; i < 32; i++)
		{
			lsl1 = (*(number1 + imm)) << (31 - i);
			lsl2 = (*(number2 + imm)) << (31 - i);
			lsr1 = lsl1 >> (31);
			lsr2 = lsl2 >> (31);
			if (lsr1 | lsr2)
			{
				*(result->number + imm) |= 1 << i;
			}
		}
		imm++;
	}
	result->compSuccess = true;
	return result;
}

t_encryptionArithmetic* encryptionArithmetic_ADD(uint32_t* number1, uint32_t* number2, t_encryptionArithmetic* result, uint16_t size)
{
	uint8_t imm = 0;
	/* allocate memory */
	result->remainder = 0;
	if (result->memAllocSuccess == false) {
		result->number = (uint32_t*)calloc(size / 32 + 1, sizeof(uint32_t));
	}
	if (result->number == NULL)
	{
		printf("Cannot allocate memory");
		result->memAllocSuccess = false;
		result->compSuccess = false;
		return result;
	}
	result->memAllocSuccess = true;

	/* Add the first 32 bits together while omitting the carry bit */
	*(result->number + imm) = *(number1 + imm) + *(number2 + imm);
	imm++;
	for (uint16_t j = 1; j < size / 32 + 1; j++)
	{
		if (*(result->number + imm - 1) < *(number1 + imm - 1))	/* check for omitted carry bit */
		{
			result->hasOverflown = true;
		}
		else
		{
			result->hasOverflown = false;
		}
		*(result->number + imm) = *(number1 + imm) + *(number2 + imm); 						//Adds the next 32 bit chunk of numbers together
		*(result->number + imm) = *(result->number + imm) + result->hasOverflown; 			//checks for the presence of a carry bit and adds it to the number
		imm++;
	}
	result->compSuccess = true;
	return result;
}

t_encryptionArithmetic* encryptionArithmetic_SUB(uint32_t* minuend, uint32_t* subtrahend, t_encryptionArithmetic* result, uint16_t size)
{
	/* based on the 1's complement subtraction */
	/* https://electricalbaba.com/1s-complement-subtraction-explained-with-examples/ */
	/* https://atozmath.com/NumberSubComp.aspx */

	/* allocate memory */
	if (result->memAllocSuccess == false) {
		result->number = (uint32_t*)calloc(size / 32 + 1, sizeof(uint32_t));
	}

	if (result->number == NULL)
	{
		printf("Cannot allocate memory");
		result->memAllocSuccess = false;
		result->compSuccess = false;
		return result;
	}
	result->memAllocSuccess = true;

	/* get actual size of the subtrahend */
	uint16_t subtrahendSize = encryptionArithmetic_numberSize(subtrahend, size);

	/* get actual size of the minuend */
	uint16_t minuendSize = encryptionArithmetic_numberSize(minuend, size);

	/* allocate memory */
	/* in order to facilitate the iterative usage of the function we need to make sure
	 * that the result is always empty when doing the calculations. Therefore both temp-pointers are needed */
	uint32_t* tempMinuend = (uint32_t*)calloc((minuendSize) / 32 + 3, sizeof(uint32_t)); 				/*  + 2 to handle the possible overflow */ //02.02: size -> minuendSize //03.02: 32 + 2 -> 32 + 3 
	uint32_t* tempSubtrahend = (uint32_t*)calloc((subtrahendSize) / 32 + 3, sizeof(uint32_t)); 			/*  + 2 to handle the possible overflow */ //02.02: size -> subtrahendSize

	if (tempMinuend == NULL || tempSubtrahend == NULL)
	{
		if (tempMinuend != NULL) {
			free(tempMinuend);
		}
		if (tempSubtrahend != NULL) {
			free(tempSubtrahend);
		}
		printf("Cannot allocate memory");
		result->memAllocSuccess = false;
		result->compSuccess = false;
		return result;
	}
	memmove(tempMinuend, minuend, (minuendSize <= 4) ? (1) : (minuendSize / 8) + 1);			/* size / 8 + 1 = number of bytes including error for int calulation */
	memmove(tempSubtrahend, subtrahend, (subtrahendSize <= 4) ? (1) : (subtrahendSize / 8) + 1);/* size / 8 + 1 = number of bytes including error for int calulation */
	result->memAllocSuccess = true;

	/* there is no sense in generating the 1's complement if we substract 0 from another number */
	if (!subtrahendSize || !minuendSize) {
		if (!subtrahendSize) {
			/* copy number from minuend into result */
			for (uint8_t imm = 0; imm < (minuendSize / 32 + 1); imm++) { //02.02: (size / 32) -> (minuendSize / 32 + 1)
				*(result->number + imm) = *(tempMinuend + imm);
			}
		}
		else {
			/* copy number from subtrahend into result */
			for (uint8_t imm = 0; imm < (subtrahendSize / 32 + 1); imm++) {	//02.02: (size / 32) -> (subtrahendSize / 32 + 1)
				*(result->number + imm) = *(tempSubtrahend + imm);
			}
		}
		free(tempMinuend);
		free(tempSubtrahend);
		result->compSuccess = true;
		return result;
	}

	/* generate the 1's complement out of the subtrahend */
	uint8_t cnt = 0;
	for (uint8_t i = 0; i < (subtrahendSize / 32) + 1; i++) { /* +1 to circumvent division by 0 */
		*(tempSubtrahend + i) = ~(*(subtrahend + i));
		cnt++;
	}
	/* clear any false set bit that occurs during 1's complement generation */
	if (subtrahendSize == 32 * (cnt - 1)) {
		*(tempSubtrahend + cnt - 1) = 0;
	}
	else if (subtrahendSize >= 32) {
		*(tempSubtrahend + cnt - 1) = *(tempSubtrahend + cnt - 1) & (0xFFFFFFFF >> ((32 * cnt) - subtrahendSize));
	}
	else {
		*(tempSubtrahend + cnt - 1) = *(tempSubtrahend + cnt - 1) & (0xFFFFFFFF >> (32 - subtrahendSize));
	}
	
	/* Add the first 32 bits together while omitting the carry bit */
	encryptionArithmetic_clearNumber(result->number, size);
	*(result->number) = *(tempMinuend) + *(tempSubtrahend);

	uint8_t imm = 1;
	for (uint16_t j = 1; j < minuendSize / 32 + 1; j++) // 02.02: +1 -> +2 | size -> minuendSize
	{
		if (*(result->number + imm - 1) < *(tempMinuend + imm - 1))	/* check for omitted carry bit */
		{
			result->hasOverflown = true;
		}
		else
		{
			result->hasOverflown = false;
		}
		*(result->number + imm) = *(tempMinuend + imm) + *(tempSubtrahend + imm); 			/* Adds the next 32 bit chunk of numbers together */
		*(result->number + imm) = *(result->number + imm) + result->hasOverflown; 			/* checks for the presence of a carry bit and adds it to the number */
		imm++;
	}

	/* get actual size of the number to catch overflows */
	uint16_t numberSize = encryptionArithmetic_numberSize(result->number, size);

	if (numberSize > minuendSize || numberSize > subtrahendSize) {
		result->hasOverflown = true;
	}
	else {
		result->hasOverflown = false;
	}

	/* if an overflow has occured (= result is positive), we need to add that bit to the LSB while omitting it as the MSB */
	if (result->hasOverflown) 
	{
		/* can't simply be a single uint32_t variable, as there would be out of boundary memory access through the ADD function */
		uint32_t* carry = (uint32_t*)calloc((numberSize) / 32 + 2, sizeof(uint32_t));	//02.02: size -> numberSize
		uint32_t* cache = (uint32_t*)calloc((numberSize) / 32 + 2, sizeof(uint32_t));	//02.02: size -> numberSize
		if (cache == NULL || carry == NULL)
		{
			if (cache != NULL) {
				free(cache);
			}
			if (carry != NULL) {
				free(carry);
			}
			printf("Cannot allocate memory");
			result->memAllocSuccess = false;
			result->compSuccess = false;
			return result;
		}
		*carry = 1;
		result->memAllocSuccess = true;
		for (int i = 0; i < (numberSize / 32) + 1; i++) 
		{
			*(cache + i) = *(result->number + i);
		}

		/* remove overflow-MSB */
		if (numberSize <= 32) 
		{
			*(cache) ^= 1 << (numberSize - 1);
		}
		else 
		{
			if (!(numberSize % 32)) /* numberSize is a multiple of 32 */
			{
				*(cache + (numberSize / 32) - 1) ^= 1 << 31;
			}
			else
			{
				*(cache + (numberSize / 32)) ^= 1 << (numberSize - 1 - ((numberSize / 32) * 32));
			}
			
		}
		encryptionArithmetic_clearNumber(result->number, size);
		encryptionArithmetic_ADD(&(*cache), &(*carry), &(*result), size);
		free(cache);
		free(carry);
	}
	else {
		/* generate the 1's complement out of the result as it is negative */
		cnt = 0;
		for (uint8_t i = 0; i < (numberSize / 32) + 1; i++) 
		{	/* +1 to circumvent division by 0 */
			*(result->number + i) = ~(*(result->number + i));
			cnt++;
		}
		if (numberSize == 32 * (cnt - 1)) 
		{
			*(result->number + cnt - 1) = 0;
		}
		else if (numberSize >= 32) 
		{
			*(result->number + cnt - 1) = *(result->number + cnt - 1) & (0xFFFFFFFF >> ((32 * cnt) - numberSize));
		}
		else 
		{
			*(result->number + cnt - 1) = *(result->number + cnt - 1) & (0xFFFFFFFF >> (32 - numberSize));
		}
	}
	free(tempMinuend);
	free(tempSubtrahend);
	result->compSuccess = true;
	return result;
}

t_encryptionArithmetic* encryptionArithmetic_DIV(uint32_t* dividend, uint32_t* divisor, t_encryptionArithmetic* result, uint16_t size)
{
	/* based on long division */
	/* https://www.cuemath.com/numbers/long-division/ */

	/* allocate memory */
	t_encryptionArithmetic* remainder, remainder2;
	remainder = &remainder2;
	encryptionArithmetic_Init(remainder, size);
	result->remainder = 0;
	bool stateFloat = false, state1 = false, isLarger = false, isEqual = false;

	/* get actual size of dividend */
	uint16_t dividendSize = encryptionArithmetic_numberSize(dividend, size);

	/* get actual size of divisor */
	uint16_t divisorSize = encryptionArithmetic_numberSize(divisor, size);

	/* basic tests to catch unwanted states */
	if (divisorSize == 0)
	{
		printf("Divisor is 0!\n");
		result->compSuccess = false;
		return result;
	}

	for (int16_t i = (dividendSize > divisorSize ? dividendSize / 32 : divisorSize / 32); i >= 0 ; i--)
	{
		/* division result = 1 */
		if (*(divisor + i) == *(dividend + i)) 
		{
			state1 = true;
			stateFloat = false;
		}
		/* would result in floating point operation */
		else if (*(dividend + i) < *(divisor + i))
		{
			state1 = false;
			stateFloat = true;
			break;
		}
		else 
		{
			state1 = false;
			stateFloat = false;
			break;
		}
	}

	if (state1) 
	{	/* division result = 1 */
		*(result->number) = 1;
		result->remainder = 0;
		encryptionArithmetic_DeInit(remainder);
		result->compSuccess = true;
		return result;
	}
	else if (stateFloat) 
	{	/* would result in floating point operation */
		printf("Divisor is larger than dividend. Floating point operations aren't supported. Result = 0\n");
		*(result->number) = 0;
		result->remainder = 0;
		encryptionArithmetic_DeInit(remainder);
		result->compSuccess = true;
		return result;
	}

	/* get the bit we are currently focused on (move from MSB to LSB) */
	/* get the offset and focused bit inside the right data chunk */
	uint16_t focusPos = 0;					/* holds the current "active" bit position we add to our remainder */
	uint8_t imm = 0;						/* holds the offset relative to the size of our data */
	uint8_t focusPosInsideDataChunk = 0;	/* holds the read position relative to the address (imm) offset */
	uint8_t cnt = 0;						/* holds the number of '0' that have to be added to the result starting from the LSB */
	uint16_t loopDivisorSize = 0;			/* holds the the loop value to catch the wrong state if divisorSize % 32 == 0 */

	if (divisorSize % 32 == 0) 
	{
		loopDivisorSize = (divisorSize / 32) - 1;
	}
	else 
	{
		loopDivisorSize = divisorSize / 32;
	}

	for (uint16_t k = 0; k < dividendSize; k++)
	{
		focusPos = dividendSize - 1 - k; 	/* -1 is needed, as the nth bit sits at position n-1 */
		imm = focusPos / 32;
		focusPosInsideDataChunk = focusPos - imm * 32;

		if (*(remainder->number + loopDivisorSize) > *(divisor + loopDivisorSize) || *(remainder->number + loopDivisorSize + 1) > *(divisor + loopDivisorSize + 1))
			/* TODO: work here -> check why it fails here */
		{
			encryptionArithmetic_SUB(remainder->number, divisor, remainder, size);	/* store subtraction of remainder and divisor inside remainder */
			encryptionArithmetic_LSL(result->number, cnt, size);					/* shift data to the left to add the next bit */
			*(result->number) |= 1;													/* add a logical "1" as LSB to the result */
			k--;																	/* keep the read position on the same level, as it would skip one otherwise */
			cnt = 0;
		}
		else if (*(remainder->number + loopDivisorSize) == *(divisor + loopDivisorSize) && *(remainder->number + loopDivisorSize + 1) == *(divisor + loopDivisorSize + 1))
		{	/* check if both numbers are actually equal and not only the MSB-chunks of data */
			isEqual = false;
			for (uint16_t i = 0; i <= loopDivisorSize; i++)
			{
				if (*(remainder->number + i) == *(divisor + i))
				{	/* add a logical "1" as LSB to the result */
					isEqual = true;
				}
				else 
				{
					/* remainder isn't == divisor and we need to execute the "else" part of the first if-statement */
					encryptionArithmetic_LSL(remainder->number, 1, size);									/* shift data one step to the left to add the next bit */
					*remainder->number |= ((*(dividend + imm) >> (focusPosInsideDataChunk) & 1) ? 1 : 0);	/* add next bit (0 or 1) to existing remainder */
					cnt++;
					isEqual = false;
					break;
				}
			}
			if (isEqual) 
			{	/* remainder == divisor and therefore the result is 1 */
				encryptionArithmetic_clearNumber(remainder->number, size);
				*(remainder->number) = 1;
			}
		}
		/* as the remainder is smaller than the divisor, we would get an overflow when subtracting.
		 * Put next bit from the dividend at the end of remainder (new LSB) and try again */
		else
		{
			encryptionArithmetic_LSL(remainder->number, 1, size);									/* shift data one step to the left to add the next bit */
			*remainder->number |= ((*(dividend + imm) >> (focusPosInsideDataChunk) & 1) ? 1 : 0);	/* add next bit (0 or 1) to existing remainder */
			cnt++;
		}
	}

	/* legacy code */

	//for (uint16_t k = 0; k < dividendSize; k++)
	//{
	//	focusPos = dividendSize - 1 - k; 	/* -1 is needed, as the nth bit sits at position n-1 */
	//	imm = focusPos / 32;
	//	focusPosInsideDataChunk = focusPos - imm * 32;

	//	/* if the remainder is larger than the divisor, we can safely conduct the subtraction w/o the need to check for an over/underflow */
	//	if (result->remainder >= *divisor)
	//	{
	//		result->remainder = result->remainder - *divisor;				/* store subtraction of remainder and divisor inside remainder */
	//		*(result->number + imm) = *(result->number + imm) << cnt | 1;	/* add a logical "1" as LSB to the result */
	//		k--;															/* keep the read position on the same level, as it would skip one otherwise */
	//		cnt = 0;
	//	}
	//	/* as the remainder is smaller than the divisor, we would get an overflow when subtracting.
	//	 * Put next bit from the dividend at the end of remainder (new LSB) and try again */
	//	else
	//	{
	//		result->remainder = (result->remainder << 1) /* shift data each cycle one step to the left to add the next bit */
	//			| ((*(dividend + imm) >> (focusPosInsideDataChunk) & 1) ? 1 : 0); /* add next bit (0 or 1) to existing remainder */
	//		cnt++;
	//	}
	//}

	/* legacy code end */

	/* get actual size of the temporary remainder */
	uint16_t remainderSize = encryptionArithmetic_numberSize(remainder->number, size);

	/* defines the amounts of loops necessary */
	uint16_t loopLength = 0;
	if (remainderSize > divisorSize) 
	{
		if (!(remainderSize % 32)) /* remainder is a multiple of 32 */
		{
			loopLength = remainderSize / 32 - 1;
		}
		else 
		{
			loopLength = remainderSize / 32;
		}
	}
	else 
	{
		if (!(divisorSize % 32)) /* remainder is a multiple of 32 */
		{
			loopLength = divisorSize / 32 - 1;
		}
		else
		{
			loopLength = divisorSize / 32;
		}
	}

	isLarger = false;
	isEqual = false;
	for (uint16_t i = 0; i <= loopLength; i++)
	{
		if (*(remainder->number + i) > *(divisor + i)) 
		{	/* store subtraction of remainder and divisor inside remainder */
			isLarger = true;
			isEqual = false;
		}
		else if (*(remainder->number + i) == *(divisor + i)) 
		{	/* add a logical "1" as LSB to the result */
			isLarger = false;
			isEqual = true;
		}
	}
	encryptionArithmetic_LSL(result->number, cnt, size);	/* shift data cnt-amount to the left */
	if (isLarger && !isEqual)	/* store subtraction of remainder and divisor inside remainder */
	{
		encryptionArithmetic_SUB(remainder->number, divisor, remainder, size);
		result->remainder = *remainder->number;				/* store subtraction of remainder and divisor inside remainder */
		*(result->number) |= 1;								/* add a logical "1" as LSB to the result */
	}
	else if (!isLarger && isEqual)	/* add a logical "1" as LSB to the result */
	{
		result->remainder = 0;								/* no remainder left */
		*(result->number) |= 1;								/* add a logical "1" as LSB to the result */
	}
	else 
	{
		result->remainder = *remainder->number;				/* store local remainder inside outbound remainder */
	}
	encryptionArithmetic_DeInit(remainder);
	result->compSuccess = true;
	return result;
}

t_encryptionArithmetic* encryptionArithmetic_MUL(uint32_t* multiplicand, uint32_t* multiplier, t_encryptionArithmetic* result, uint16_t size)
{
	/* based on the shift and add algorithm  https://users.utcluj.ro/~baruch/book_ssce/SSCE-Shift-Mult.pdf */
	uint8_t imm = 0;		/* holds the immediate value to keep track of how many chunks of data we traversed */
	uint8_t readPos = 0; 	/* current readable bit position */

	/* get actual size of the multiplicand */
	uint16_t multiplicandSize = encryptionArithmetic_numberSize(multiplicand, size);

	/* get actual size of the multiplier */
	uint16_t multiplierSize = encryptionArithmetic_numberSize(multiplier, size);

	/* allocate memory */
	/* in order to facilitate the iterative usage of the function we need to make sure
	 * that the result is always empty when doing the calculations. Therefore both temp-pointers are needed */
	uint32_t* cache = (uint32_t*)calloc((size) / 32 * 3 + 2, sizeof(uint32_t)); 	/* Has to be double the size to be able to store all bits + 2 to handle the possible overflow */
	uint32_t* tempMultiplicand; /* allocation is done below */
	uint32_t* tempMultiplier;	/* allocation is done below */

	/* for some reason (has to be investigated in the future) multiplying a number with a smaller number results in a limited amount of flipped bits
	 * current workaround: swap multiplier and multiplicand if multiplicand > multiplier */
	if (multiplicandSize + 4 > multiplierSize) {
		tempMultiplicand = (uint32_t*)calloc((multiplicandSize) / 32 * 4 + 2, sizeof(uint32_t));		/* Has to be double the size to be able to store all bits + 2 to handle the possible overflow */ //02.02: size -> multiplicandSize
		tempMultiplier = (uint32_t*)calloc((multiplicandSize) / 32 * 4 + 2, sizeof(uint32_t)); 		/* Has to be double the size to be able to store all bits + 2 to handle the possible overflow */ //02.02: size -> multiplierSize
		if (cache == NULL || tempMultiplicand == NULL || tempMultiplier == NULL)
		{
			if (cache != NULL) {
				free(cache);
			}
			if (tempMultiplicand != NULL) {
				free(tempMultiplicand);
			}
			if (tempMultiplier != NULL) {
				free(tempMultiplier);
			}
			printf("Cannot allocate memory");
			result->memAllocSuccess = false;
			result->compSuccess = false;
			return result;
		}
		memmove(tempMultiplier, multiplicand, (multiplicandSize <= 4) ? (1) : (multiplicandSize / 8) + 1);		/* size / 8 = number of bytes */
		memmove(tempMultiplicand, multiplier, (multiplicandSize <= 4) ? (1) : (multiplicandSize / 8) + 1);		/* size / 8 = number of bytes */
		/* as the numbers have swapped places, a size recalculation is needed */
		multiplicandSize = encryptionArithmetic_numberSize(multiplier, size);
		multiplierSize = encryptionArithmetic_numberSize(multiplicand, size);
	}
	else { /* used to be the default way */
		tempMultiplicand = (uint32_t*)calloc((multiplicandSize) / 32 * 4 + 2, sizeof(uint32_t)); 		/* Has to be double the size to be able to store all bits + 2 to handle the possible overflow */ //02.02: size -> multiplicandSize
		tempMultiplier = (uint32_t*)calloc((multiplierSize) / 32 * 4 + 2, sizeof(uint32_t)); 			/* Has to be double the size to be able to store all bits + 2 to handle the possible overflow */ //02.02: size -> multiplierSize
		if (cache == NULL || tempMultiplicand == NULL || tempMultiplier == NULL)
		{
			if (cache != NULL) {
				free(cache);
			}
			if (tempMultiplicand != NULL) {
				free(tempMultiplicand);
			}
			if (tempMultiplier != NULL) {
				free(tempMultiplier);
			}
			printf("Cannot allocate memory");
			result->memAllocSuccess = false;
			result->compSuccess = false;
			return result;
		}
		memmove(tempMultiplicand, multiplicand, (multiplicandSize <= 4) ? (1) : (multiplicandSize / 8) + 1);	/* size / 8 = number of bytes */
		memmove(tempMultiplier, multiplier, (multiplierSize <= 4) ? (1) : (multiplierSize / 8) + 1);			/* size / 8 = number of bytes */
	}
	result->memAllocSuccess = true;

	/* clear result for next operation */
	encryptionArithmetic_clearNumber(result->number, size);

	/* traverse the saved number in 32bit chunks */
	for (uint16_t j = 0; j <= multiplierSize; j++) //02.02: size -> multiplierSize
	{
		if ((j / (imm + 1)) == 32) { /* +1 to prevent division by 0 */
			readPos = 0;
			imm++;
		}
		/* check if the current LSB is '1', skip if not */
		if ((*(tempMultiplier + imm) >> readPos) & 1)
		{
			for (uint8_t m = 0; m <= ((multiplicandSize + multiplierSize) / 32); m++)
			{
				/* shift the 32bit number "chunk" according the the layer of addition we are currently on and store in cache */
				if (readPos == 0) {
					*(cache + (m + (j / 32))) += *(tempMultiplicand + m);	/* assign bits depending on current readPos */
				}
				else {
					*(cache + (m + j / 32)) += *(tempMultiplicand + m) << readPos;				/* assign bits depending on current readPos */
					*(cache + (m + j / 32) + 1) += *(tempMultiplicand + m) >> (32 - readPos);	/* assign bits that would be lost in shift operation to the next "chunk" */
				}
				/* add the previous result to the new layer including any overflowing that might occur */
				*(result->number + m) += *(cache + m) + result->hasOverflown;
				/* if the addition generated an overflow, the stored result will be smaller than the number we added to it */
				if ((*(result->number + m) < *(cache + m))) {
					result->hasOverflown = true;
				}
				else {
					result->hasOverflown = false;
				}
				
			}
			/* clearing the cache for the next addition operation */
			encryptionArithmetic_clearNumber(cache, size);
		}
		readPos++;
	}
	free(cache);
	free(tempMultiplicand);
	free(tempMultiplier);
	result->compSuccess = true;
	return result;
}

void encryptionArithmetic_LSL(uint32_t* number, uint8_t amount, uint16_t size) 
{	
	uint32_t overflowBit1 = 0, overflowBit2 = 0;
	/* get actual size of number */
	uint16_t numberSize = encryptionArithmetic_numberSize(number, size);

	overflowBit1 = *number >> (32 - amount);		/* store MSB from first block */
	*number = *number << amount;					/* perform lsl-operation of first block */
	if (numberSize + amount >= 32)
	{
		for (uint8_t i = 1; i <= (numberSize + amount) / 32; i++)
		{
			overflowBit2 = *(number + i) >> (32 - amount);		/* store MSB from current block */
			*(number + i) = *(number + i) << amount;			/* perform lsl-operation of current block */
			*(number + i) |= overflowBit1;						/* add MSB of last block to current block */
			overflowBit1 = overflowBit2;						/* store current MSB for next iteration */
		}
	}
}

t_encryptionArithmetic* encryptionArithmetic_Init(t_encryptionArithmetic* result, uint16_t size)
{
	result->number = (uint32_t*)calloc((size) / 32 * 4 + 1, sizeof(uint32_t)); /* has to be this size to accommodate all types of computations, including multiplication */
	if (result->number == NULL)
	{
		printf("Cannot allocate memory");
		result->memAllocSuccess = false;
		result->compSuccess = false;
		return result;
	}
	result->memAllocSuccess = true;
	result->compSuccess = false;
	*(result->number) = 0;
	result->remainder = 0;
	result->hasOverflown = false;
	return result;
}

void encryptionArithmetic_DeInit(t_encryptionArithmetic* ptr)
{
	deinitCounter++;
	free(ptr->number);
}

uint16_t encryptionArithmetic_numberSize(uint32_t *number, uint16_t size) {
	uint8_t cnt = 0;
	uint16_t sizeCache = size * 3, actualLength = size * 3; //02.02: *2 -> *3
	while ((*(number + (sizeCache / 32) - 1)) >> (31 - cnt) != 1)
	{
		cnt++;
		actualLength--;
		if (cnt == 32)
		{
			cnt = 0;
			sizeCache -= 32;
		}
		if (!actualLength) {
			break;
		}
	}
	return actualLength;
}

void encryptionArithmetic_clearNumber(uint32_t* number, uint16_t size) {
	uint16_t numberSize = encryptionArithmetic_numberSize(number, size);
	uint16_t loopNumberSize = 0;
	if (numberSize % 32) 
	{
		loopNumberSize = numberSize - 1;
	}
	else 
	{
		loopNumberSize = numberSize;
	}

	for (uint16_t i = 0; i <= loopNumberSize / 32; i++) {
		*(number + i) = 0;
	}
}

/* returns true if first number is larger than the second one */
bool encryptionArithmetic_isLarger(uint32_t* number1, uint32_t* number2, uint16_t size) {
	uint16_t divisorSize = encryptionArithmetic_numberSize(number2, size);
	bool isLarger = false;
	for (int16_t i = (divisorSize / 32); i >= 0; i--)
	{	/* i has to be signed to access 0 */
		if (*(number1 + i) > *(number2 + i))
		{
			isLarger = true;
		}
		else
		{
			isLarger = false;
		}
	}
	return isLarger;
}

bool encryptionArithmetic_stringToHex(char* src, uint32_t* dest, uint16_t length) {
	/* holds the amount of characters that are stored in src */
	uint16_t stringLength = 0;

	/* clear destination in order to facilitate iterative use or generally the same pointer */
	encryptionArithmetic_clearNumber(dest, length);

	/* get actual string length without the '\0' character and convert any lowercase characters */
	for (stringLength = 0; src[stringLength] != '\0'; ++stringLength) 
	{
		switch (src[stringLength]) {
		case 'a': src[stringLength] = 'A'; break;
		case 'b': src[stringLength] = 'B'; break;
		case 'c': src[stringLength] = 'C'; break;
		case 'd': src[stringLength] = 'D'; break;
		case 'e': src[stringLength] = 'E'; break;
		case 'f': src[stringLength] = 'F'; break;
		default: break;
		}
		if (src[stringLength] < 48 || (src[stringLength] > 57 && src[stringLength] < 65) || src[stringLength] > 70) 
		{	/* 0...9, A...F */
			printf("Abort, as input string contains values other than 0-9, A-F\n");
			return false;
		}
	}
	if (!stringLength) 
	{	/* string is empty */
		return false;
	}
	if (stringLength * 4 > length) 
	{
		printf("Abort, as stringLength > maxAllocatedSpace and would result in faulty memory access\n");
		return false;
	}

	/* store conversion of string in destination while reversing the sequence */
	uint16_t imm = stringLength / 8; /* 8*char == 32 bit */
	uint8_t cnt;
	if (!(stringLength % 8))
	{
		imm = stringLength / 8 - 1;
	}
	else 
	{
		imm = stringLength / 8;
	}
	cnt = stringLength - (imm * 8) - 1;

	for (uint16_t cntUp = 0; cntUp < stringLength; cntUp++) 
	{
		if (src[cntUp] >= 48 && src[cntUp] <= 57)
		{ /* 0...9 */
			*(dest + imm) |= (src[cntUp] - 0x30) << (cnt * 4);
		}
		else 
		{ /* A...F */
			*(dest + imm) |= (src[cntUp] - 0x37) << (cnt * 4);
		}
		if (cnt == 0)
		{
			cnt = 8;
			imm--;
		}
		cnt--;
	}
	return true;
}

void encryptionArithmetic_copyNumber(uint32_t* src, uint32_t* dest, uint16_t length) {
	encryptionArithmetic_clearNumber(dest, length);
	uint32_t srcSize = encryptionArithmetic_numberSize(src, length);
	memmove(dest, src, (srcSize <= 4) ? (1) : (srcSize / 8) + 1);			/* size / 8 + 1 = number of bytes including error for int calulation */ //06.02: +1 -> + 0
}