#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#define BYTE_WIDTH 8
#define REMAINDER_WIDTH 8
#define MESSAGE_LENGTH 3
#define LOOKUP_TABLE_DATA "CRC-8.dat"
#define TWO_UP_EIGHT 256

// Define a boolean type as an enumeration.
typedef enum { FALSE = 0, TRUE = 1 } Bool;

const unsigned char PowersOfTwo[REMAINDER_WIDTH] = {1, 2, 4, 8, 16, 32, 64, 128};

// This truncated "Divisor", when displayed in binary, looks like this (11010101).
const unsigned char Poly = 213;

// This function performs a Byte-wise "LookupTable" CRC calculation on "NumberOfBytes" Bytes, starting at "DataMessage".
// The divisor used is defined in the constant "Poly".
// The value returned by the function is the "CRC-Digest".
unsigned char LookupTableCrc(const unsigned char *DataMessage, unsigned int NumberOfBytes, const unsigned char *LookupTable){
	int X;
	// Because looking up "0" in the table returns "0", it is safe to use a table lookup to fill the "WorkingRegister" with its initial "DataMessage" value.
	register unsigned char WorkingRegister = 0;
	// Query the "LookupTable" exactly "NumberOfBytes" times.  Perform lookups using the value inside of "WorkingRegister" as the index.
	// After each table query, "XOR" the value returned by "LookupTable" with the next most significant Byte in "DataMessage".
	// "X" is the location of the next data Byte to pull into the calculation.
	for ( X = 0; X < NumberOfBytes; X++ ) WorkingRegister = LookupTable[WorkingRegister] ^ DataMessage[X];
	return WorkingRegister;
}

// Simply print out "ThisByte" using "1"s and "0"s.
void PrintByteInBinary(unsigned char ThisByte){
	unsigned int X;
	char HoldOut[BYTE_WIDTH + 1];
	for ( X = 0; X < BYTE_WIDTH; X++ ) HoldOut[X] = (ThisByte & PowersOfTwo[BYTE_WIDTH - 1 - X])? '1': '0';
	HoldOut[BYTE_WIDTH] = '\0';
	printf("%s", HoldOut);
}

int main(){
	unsigned int X;
	// Include an extra Byte in the message to test the trailing-zeros CRC property.
	unsigned char DemonstrationMessage[MESSAGE_LENGTH + 1] = {202, 45, 166, 0};
	unsigned char TheDigest;
	unsigned char *TheLookupTable = (unsigned char*)malloc(TWO_UP_EIGHT*sizeof(unsigned char));
	FILE *TableFile;
	
	// Read the precompiled lookup-table from "CRC-8.dat" directly into "TheLookupTable".
	TableFile = fopen(LOOKUP_TABLE_DATA, "rb");
	fread(TheLookupTable, sizeof(unsigned char), TWO_UP_EIGHT, TableFile);
	fclose(TableFile);
	
	printf("\n---------------------------------------------------------------------------------\n");
	
	printf("\nBehold the |%d| Byte DataMessage - |", MESSAGE_LENGTH);
	for ( X = 0; X < MESSAGE_LENGTH; X++ ) {
		PrintByteInBinary(DemonstrationMessage[X]);
		printf("|");
	}
	printf("\n\n");
	
	TheDigest = LookupTableCrc(DemonstrationMessage, MESSAGE_LENGTH*sizeof(unsigned char), TheLookupTable);
	printf("Behold TheDigest-|%d|-|", TheDigest);
	PrintByteInBinary(TheDigest);
	printf("|\n");
	
	printf("\n---------------------------------------------------------------------------------\n");
	
	// Calculate "TheDigest" with appended zeros, and then replace the trailing zeros with this number.
	// Recalculate "TheDigest", and this time the digest should compute to zero.
	
	printf("\nAppend \"W\" zeros to the DataMessage - |");
	for ( X = 0; X < (MESSAGE_LENGTH + 1); X++ ) {
		PrintByteInBinary(DemonstrationMessage[X]);
		printf("|");
	}
	printf("\n\n");
	
	TheDigest = LookupTableCrc(DemonstrationMessage, MESSAGE_LENGTH*sizeof(unsigned char) + 1, TheLookupTable);
	printf("Behold TheDigest-|%d|-|", TheDigest);
	PrintByteInBinary(TheDigest);
	printf("|\n");
	
	printf("\n---------------------------------------------------------------------------------\n");
	
	DemonstrationMessage[MESSAGE_LENGTH] = TheDigest;
	printf("\n\"TheDigest\" of this DataMessage should be zero - |");
	for ( X = 0; X < (MESSAGE_LENGTH + 1); X++ ) {
		PrintByteInBinary(DemonstrationMessage[X]);
		printf("|");
	}
	printf("\n\n");
	
	TheDigest = LookupTableCrc(DemonstrationMessage, MESSAGE_LENGTH*sizeof(unsigned char) + 1, TheLookupTable);
	printf("Behold TheDigest-|%d|-|", TheDigest);
	PrintByteInBinary(TheDigest);
	printf("|\n");
	
	printf("\n---------------------------------------------------------------------------------\n");
	printf("\n");
	
	return 0;
}

