#include <cstdlib>
#include <cstdint>
#include <cstdio>
#include <time.h>
#include <stdlib.h>
#include <string>

#define T_LEN  5
#define GEN_POLY 0x0D
#define GEN_LEN 4
#define CODEW_LEN 17
#define MESS_LEN 14

int transmission[T_LEN] = {0x123, 0x456, 0x789, 0xABC, 
			       0xDEF};

std::string CRCstrings[2] = {"has", "has not"};

std::string ERRstrings[2] = {"with no errors", "with an error"};

void seed_rng()
{
	srand(time(NULL));
}

void introduce_error(int* transmission)
{
	int codeword = (rand() % T_LEN);
	int mask = 0x01 << (rand() % CODEW_LEN);

	transmission[codeword] ^= mask;
}

int find_msb_posit(int codeword)
{
	int shift =  (CODEW_LEN - 1);
	int mask = 0x01 << shift;
	
	while ((codeword & mask) == 0)
	{
		mask >>= 1;
		shift -= 1;
	}

	return shift;
}

int shift_message(int message)
{
	return message << (CODEW_LEN - MESS_LEN);
}

int divide_by_generator(int codeword)
{
	int remMask = (0x01 << (CODEW_LEN - MESS_LEN)) - 1;

	int currentWord = codeword;
	

	while ((currentWord & (remMask ^ 0xFFFFFFFF)) != 0) // 0xFFFFFFFF is used here because ints are 32 bit by default
		currentWord ^= (GEN_POLY << (find_msb_posit(currentWord) - GEN_LEN + 1));
	
	//could have used recursion here but didn't because I was concerned about performance (as if the junk above is performant, lol)
	
	return (currentWord & remMask);
}

int find_codeword(int message)
{
	message = shift_message(message);

	return message | divide_by_generator(message);
}

bool check_codeword(int codeword)
{
	return (divide_by_generator(codeword) == 0); 
}

bool check_transmission(int* transmission)
{
	for(int i = 0; i < T_LEN; i++)
		if (!check_codeword(transmission[i]))
			return false;
	return true;
}

void convert_transmission(int* transmission)
{
	for(int i = 0; i < T_LEN; i++)
		transmission[i] = find_codeword(transmission[i]);
}

void print_transmission(int* transmission)
{
	for(int i = 0; i < T_LEN; i++)
		printf("%x ", (transmission[i] & 0x1FFF8) >> 3); 

	printf("     ");

	for(int i = 0; i < T_LEN; i++)
		printf("%x ", transmission[i]); 

	printf("\n");
}

int main()
{

	seed_rng();

	bool error = rand() % 2;

	convert_transmission(transmission);

	printf("original transmission:\n");
	print_transmission(transmission);

	if (error) 
	introduce_error(transmission);

	printf("the transmission %s:\n", ERRstrings[error].c_str());
	print_transmission(transmission);

	printf("the CRC %s detected an error\n", CRCstrings[check_transmission(transmission)].c_str());
}
