crc.c-64 Bit Cyclic Redundant Code-XOR Long Division To Bytewise Table Lookup

Introduction

When a “DataMessage” is transmitted through potentially noisy channels, it makes sense to check for easily detectable errors.  While there are many ways to accomplish this goal, the crc standard wins because it maintains low false-positive probabilities, and at the same time, keeps computation confined to table lookups and single instruction “XOR”s .

It is easy for an enthusiast, with a high-school education, to understand how a crc-digest is just the remainder when a bit-stream is treated as a single number and then long-divided by an engineered divisor using carry-less “XOR” subtraction.  This procedure is logical and clearly defined, but the seasoned computer programmer will anticipate optimizations, which transform a brute-force method into the high-level C-code used in production software.

Looping to handle and shift each bit is possible, but slow.  Thus, a 256-value lookup-table is used to segment the calculation into 8-bits per loop.  A computer-machine with addressable Bytes will benefit from this chosen segmentation width.  Additionally, storing a 256 element table, each value consisting of “W” bits, is not costly when compared with the resources of modern computers.  The veteran programmer will understand how the X86-X64 architecture, with its Little-Endian Byte-Order, complicates the modification of a “DataMessage” to set its “CRC-Digest” to zero.

Below, the reader will encounter some simple mathematics, and a proof, that when given the same “DataMessage”, modulo-2 long-division and the Bytewise lookup-table-procedure, generate the same “CRC-Digest”.  All of the information below is certainly available on the internet, but is not easily found on one web page.  This single web page will clarify the mathematics and C-code, which make CRC fast, and therefore very appealing.  Also, the C-code included was written to resemble textbook stock.

Another important use for a CRC-digest is to easily identify small differences in long Byte strings, which are otherwise identical.  Skip forward to the Conclusion Section for the 17x17 Challenge prototype example.

Investigation

Modulo-2 XOR Mathematics:

It should come as no surprise that a “CRC-Digest” is the remainder after some sort of division-like procedure.  When small changes are made to the digits of an enormous dividend, and that number is then divided by a reasonably-large divisor, the new calculated remainder will be garbled, and likely much different than the correct remainder.  Further, a method for carpentering a false-positive, although possible, is not immediately obvious.  Later, it will be shown that modulo-2 polynomial multiplication, where each bit represents the corresponding-power coefficient, can be used to generate false-positives.  This mathematical genre leads to naming the divisor, “Poly”.  Modulo-2 long-division is superior to actual long-division for CRC calculation, primarily because the only condition required to zero the Most Significant Bit(MSB) during a subtraction, is that both “MSB”s are “1”.  In actual long-division, when zeroing the “MSB” of the “CurrentRemainder”, the process of borrowing from higher columns demands additional comparisons, and often bit-width disparity.  Fortunately, there is no functional need for an actual valid remainder when checking the integrity of a “DataMessage”.  In conclusion, the modulo-2 remainder is a hard-to-plausible-false-positive, is easy to calculate, is manageable-in-size, and is a unique number to associate with a “DataMessage”.  It will be shown that a crude false-positive message is very easy to generate, knowing the size of “DataMessage”, the “Poly” being used, and the expected “CRC-Digest”.  A plausible false-positive, on the other hand, requires an intact “DataMessage”, the “Poly”, and computation based on solid knowledge of modulo-2 polynomial multiplication.  The knowledge needed can be learned on this web page.

Prior to formalizing the steps involved in calculating the “CRC-Digest” of a “DataMessage”, it is useful to refine “modulo-2 XOR arithmetic” down to a set of rules that define its essence:

• 0 XOR 0 = 0
• 0 XOR 1 = 1
• 1 XOR 0 = 1
• 1 XOR 1 = 0
• { “0”, “1” } Are the two allowable, opposite “states” for all digits.
• In the expression ( C = A XOR B ), Cn = An XOR Bn for the n’th digit.  Thus, the digits from each column are independent.
• Trivial but important, ( A XOR A = 0 ), and ( A XOR 0 = A ).
• An XOR performed with “0” means “hold current state”.
• An XOR performed with “1” translates to “flip current state”.
• “XOR” is equally both { +, - }, therefore if ( C = A XOR B ), then ( B = C XOR A ), and ( A = B XOR C ).
• The Number of “0”s in an “XOR” column is meaningless.
• The parity of “1”s in an “XOR” column determines the result.  ( EVEN = “0”, ODD = “1”).
• Consider the expression ( E = A XOR B XOR C XOR D ), for one Byte, and “Order DOES NOT Matter”.
 0 0 0 0 C6 A5 B4 A3 0 D1 B0 B7 A6 0 C4 B3 A2 C1 0 A7 B6 C5 A4 0 D2 A1 C0 C7 D6 D5 0 D3 C2 B1 D0 XOR D7 0 B5 D4 C3 B2 0 A0 ----- ---- ---- ---- ---- ---- ---- ---- ---- E7 E6 E5 E4 E3 E2 E1 E0

Step 1 - Traditional Modulo-2 Bitwise Binary-Long-Division:

The example calculation carried out below will generate an 8-bit-wide CRC remainder to keep it short.  A 3 Byte “DataMessage” is used, so as to clearly demonstrate the eventual “bitwise” to “Bytewise” transition.

• 1 register holds the “CurrentRemainder” of width “W” bits.
• The first “W” bits, of the “DataMessage” being processed, will be the initial value of “CurrentRemainder”.
• “Poly” holds the divisor value for the calculation, which has (“W” + 1) bits.  The “MSB” of “Poly” must be “1”, so only “W” bits need to be represented.
• The entire CRC calculation is governed by the “MSB” inside of “CurrentRemainder”.
• If the “MSB” is “0”, then “CurrentRemainder” is left-shifted by one bit, and the “NextMessageBit” of “NextMessageByte” is pulled into the register from the least-significant-side.  Information about the next message bit to be pulled is then incremented.
• If the “MSB” is “1”, it will line-up with the token “1” bit, omitted from the divisor, stored inside “Poly”.  Thus, the “CurrentRemainder” needs to be left-shifted by one position, with the corresponding “NextMessageBit” pull, before the register is “XOR”ed with “Poly”.  This value becomes the new “CurrentRemainder”.
• In the end, “CurrentRemainder” is the “CRC-Digest”.  The CRC calculation is over when one of two events happens:
• End Event #1) “MSB” is “0”, and the final “NextMessageBit” is pulled into “CurrentRemainder”.
• End Event #2) The “MSB” is “1”, and the final “NextMessageBit” is pulled into “CurrentRemainder”, then the “XOR” with “Poly” is carried out, and the process is over.

The following values are used in the worked example below:

• The “Divisor” is standard CRC-8 = ( X8+X7+X6+X4+X2+1 ) = ( 111010101 ), ∴ “Poly” = ( 11010101 ).
• The “DataMessage”, or “Dividend” is the arbitrary 3-Byte bit-string = ( 11001010|00101101|10100110 ).
• The “Quotient” will not be compiled during CRC calculation, but it will assist in generating “Plausible-False-Positive”s. • Note that the above example exits on “End Event #1”, and in decimal, the “CRC-Digest” is 90.
• Inspect the output of Clear-CRC-8.c below to verify the the correctness of the demonstration example.
 --------------------------------------------------------------------------------- Behold the |3| Byte DataMessage - |11001010|00101101|10100110| End Event #1 Behold TheDigest-|90|-|01011010| --------------------------------------------------------------------------------- Append "W" zeros to the DataMessage - |11001010|00101101|10100110|00000000| End Event #2 Behold TheDigest-|153|-|10011001| --------------------------------------------------------------------------------- "TheDigest" of this DataMessage should be zero - |11001010|00101101|10100110|10011001| End Event #2 Behold TheDigest-|0|-|00000000| ---------------------------------------------------------------------------------

Step 2 - Byte-Isolated Bitwise Modulo-2 Binary-Long-Division:

The second calculation carried out by hand uses the same “DataMessage” and “Poly”.  The calculated “CRC-Digest” is the same, because each “XOR” column has an equal “1” bit-parity to the corresponding column in the first example.  The big transition is that each Byte in the “DataMessage” is isolated.  Token zeros are pulled into the “CurrentRemainder” register, and as a result, we can highlight the numbers, which will become the lookup-table index and return values.  The algorithm for lookup-table creation is now plain to see. Step 3 - Transition From Bitwise Long-Division To XOR Lookup-Table:

The calculation above demonstrates and reinforces that the addition of extra “0”s to any column in the “XOR” cascade will have no effect on the final“CRC-Digest”.  Thus, as the most significant Byte is removed from the “WorkingRegister”, the next “DataMessage” Byte is drawn into “WorkingRegister” from the least-significant-side, and the new register value is “XOR”ed with the number returned from the lookup table.  This continues until the final “DataMessage” Byte has been pulled in, and then the value inside of “WorkingRegister” is the “CRC-Digest”.

Step 4 - Byte-Isolated Lookup-Table CRC-Digest Calculation:

This algorithm is short, streamlined, and very appealing.  Just start with a “WorkingRegister” of width “W”, and fill it with “0”s.  Then pull out a Byte from the most-significant-side of the register, using it to index the lookup-table.  Pull the next “DataMessage” Byte into the least-significant-side of the register, and then “XOR” the “WorkingRegister” with the value returned by the lookup-table, back into the register.  When zero is used to index the lookup-table, a zero is returned, and thus the “XOR” has no effect; the next “DataMessage” Byte being pulled in from the least-significant-side is all that happens.  This loop continues until the final “DataMessage” Byte is pulled in and the last “XOR” operation is carried out.  Refer to crc.c for the working C-code.  Below, you will find the CRC-8 and CRC-64 lookup-tables.  Find the code used to create the tables here:  Compile-CRC-8-Lookup-Table.c, and here Compile-CRC-64-Lookup-Table.c.  Notice that the CRC-8 lookup-table represents a perfect and complete 1-to-1 function.  This will be explored in the next section.

 |  0|-|00| |  1|-|D5| |  2|-|7F| |  3|-|AA| |  4|-|FE| |  5|-|2B| |  6|-|81| |  7|-|54| |  8|-|29| |  9|-|FC| | 10|-|56| | 11|-|83| | 12|-|D7| | 13|-|02| | 14|-|A8| | 15|-|7D| | 16|-|52| | 17|-|87| | 18|-|2D| | 19|-|F8| | 20|-|AC| | 21|-|79| | 22|-|D3| | 23|-|06| | 24|-|7B| | 25|-|AE| | 26|-|04| | 27|-|D1| | 28|-|85| | 29|-|50| | 30|-|FA| | 31|-|2F| | 32|-|A4| | 33|-|71| | 34|-|DB| | 35|-|0E| | 36|-|5A| | 37|-|8F| | 38|-|25| | 39|-|F0| | 40|-|8D| | 41|-|58| | 42|-|F2| | 43|-|27| | 44|-|73| | 45|-|A6| | 46|-|0C| | 47|-|D9| | 48|-|F6| | 49|-|23| | 50|-|89| | 51|-|5C| | 52|-|08| | 53|-|DD| | 54|-|77| | 55|-|A2| | 56|-|DF| | 57|-|0A| | 58|-|A0| | 59|-|75| | 60|-|21| | 61|-|F4| | 62|-|5E| | 63|-|8B| | 64|-|9D| | 65|-|48| | 66|-|E2| | 67|-|37| | 68|-|63| | 69|-|B6| | 70|-|1C| | 71|-|C9| | 72|-|B4| | 73|-|61| | 74|-|CB| | 75|-|1E| | 76|-|4A| | 77|-|9F| | 78|-|35| | 79|-|E0| | 80|-|CF| | 81|-|1A| | 82|-|B0| | 83|-|65| | 84|-|31| | 85|-|E4| | 86|-|4E| | 87|-|9B| | 88|-|E6| | 89|-|33| | 90|-|99| | 91|-|4C| | 92|-|18| | 93|-|CD| | 94|-|67| | 95|-|B2| | 96|-|39| | 97|-|EC| | 98|-|46| | 99|-|93| |100|-|C7| |101|-|12| |102|-|B8| |103|-|6D| |104|-|10| |105|-|C5| |106|-|6F| |107|-|BA| |108|-|EE| |109|-|3B| |110|-|91| |111|-|44| |112|-|6B| |113|-|BE| |114|-|14| |115|-|C1| |116|-|95| |117|-|40| |118|-|EA| |119|-|3F| |120|-|42| |121|-|97| |122|-|3D| |123|-|E8| |124|-|BC| |125|-|69| |126|-|C3| |127|-|16| |128|-|EF| |129|-|3A| |130|-|90| |131|-|45| |132|-|11| |133|-|C4| |134|-|6E| |135|-|BB| |136|-|C6| |137|-|13| |138|-|B9| |139|-|6C| |140|-|38| |141|-|ED| |142|-|47| |143|-|92| |144|-|BD| |145|-|68| |146|-|C2| |147|-|17| |148|-|43| |149|-|96| |150|-|3C| |151|-|E9| |152|-|94| |153|-|41| |154|-|EB| |155|-|3E| |156|-|6A| |157|-|BF| |158|-|15| |159|-|C0| |160|-|4B| |161|-|9E| |162|-|34| |163|-|E1| |164|-|B5| |165|-|60| |166|-|CA| |167|-|1F| |168|-|62| |169|-|B7| |170|-|1D| |171|-|C8| |172|-|9C| |173|-|49| |174|-|E3| |175|-|36| |176|-|19| |177|-|CC| |178|-|66| |179|-|B3| |180|-|E7| |181|-|32| |182|-|98| |183|-|4D| |184|-|30| |185|-|E5| |186|-|4F| |187|-|9A| |188|-|CE| |189|-|1B| |190|-|B1| |191|-|64| |192|-|72| |193|-|A7| |194|-|0D| |195|-|D8| |196|-|8C| |197|-|59| |198|-|F3| |199|-|26| |200|-|5B| |201|-|8E| |202|-|24| |203|-|F1| |204|-|A5| |205|-|70| |206|-|DA| |207|-|0F| |208|-|20| |209|-|F5| |210|-|5F| |211|-|8A| |212|-|DE| |213|-|0B| |214|-|A1| |215|-|74| |216|-|09| |217|-|DC| |218|-|76| |219|-|A3| |220|-|F7| |221|-|22| |222|-|88| |223|-|5D| |224|-|D6| |225|-|03| |226|-|A9| |227|-|7C| |228|-|28| |229|-|FD| |230|-|57| |231|-|82| |232|-|FF| |233|-|2A| |234|-|80| |235|-|55| |236|-|01| |237|-|D4| |238|-|7E| |239|-|AB| |240|-|84| |241|-|51| |242|-|FB| |243|-|2E| |244|-|7A| |245|-|AF| |246|-|05| |247|-|D0| |248|-|AD| |249|-|78| |250|-|D2| |251|-|07| |252|-|53| |253|-|86| |254|-|2C| |255|-|F9|

Using lookup-tables, as presented above, is the key to fast CRC Calculation.  The “LookupTableCrc” method contained in Lookup-Table-CRC-8.c is easy on the eyes, and relies entirely on array indexing and single-instruction “XOR”s.  The only distinction between modulo-2-binary-long-division and CRC-table-lookup, is that a Byte-worth of extra zeros is pulled into the calculation per table-lookup.  Extra zeros were shown to have no effect on the final result in the Modulo-2 XOR Mathematics Section.  Notice how the lookup-table program arrives at the same “CRC-Digest”s:

 --------------------------------------------------------------------------------- Behold the |3| Byte DataMessage - |11001010|00101101|10100110| Behold TheDigest-|90|-|01011010| --------------------------------------------------------------------------------- Append "W" zeros to the DataMessage - |11001010|00101101|10100110|00000000| Behold TheDigest-|153|-|10011001| --------------------------------------------------------------------------------- "TheDigest" of this DataMessage should be zero - |11001010|00101101|10100110|10011001| Behold TheDigest-|0|-|00000000| ---------------------------------------------------------------------------------

Step 5 - Creating Plausible False-Positive “DataMessage”s:

In order to generate a “FalsePositiveDataMessage” with minimal bit-alterations, one must first understand the mathematics that govern “CRC-Digest” calculation.  Binary polynomial coefficient multiplication is demonstrated in the hand calculation below, and as with modulo-2 “XOR” division, “1”-bit-parity determines each power-column result.  When “Poly” is multiplied by the “CalculatedQuotient”, followed by “XOR”ing the “CORRECT_CRC_DIGEST” to the end of the product, what do we get?  We get the original “DataMessage”. The calculation presented above may seem quite harmless to the casual observer, but upon closer inspection, it proves that “CRC-8” will not allow for “Quotient”-driven product-alterations confined to“W” bits.  This conclusion is reached, because the calculation above will yield every possible “FalsePositiveDataMessage” by altering the “Quotient”, and due to the “XOR” row offsets, altered bits in “TheProduct” will span at least “W + 1” bits.  This seemingly inane proof will lead to the important fact:

Every value in the “CRC-8” Lookup-Table is unique:

Imagine that the numbers contained in the lookup-table are simply the remainders calculated when the corresponding index value “B” has “W” zeros tacked onto the end of it like this:  ( b7b6b5b4b3b2b1b000000000 ).  Now, since only “W” bits are subject to alteration, the remainder calculated using “CRC-8” will always be unique.  As a result, the CRC-8 lookup-table, presented in Step 4, appears to be a randomly shuffled, perfect and complete, “1-To-1 Function”.

The Straightforward False-Positive Generator Method:

• Only two values are required to execute this procedure:  The correct “DataMessage”, and the “Poly” being used.  The first step is to use these two numbers to calculate the “Quotient”, which will be “W” bits shorter than “DataMessage”.  The original “Remainder”, or correct “CRC-Digest” is also required.

• Modify the “Quotient”, then mod-2-polynomial-multiply it with “Poly” * See the hand calculation above.  Finish by “XOR”ing the original “Remainder” into the end of “TheProduct” to get “FalsePositiveDataMessage”.

• Based on the relative position of “1”s in “Poly”, choose the “Quotient” bits to flip, so as to minimize the number of altered bits in “FalsePositiveDataMessage”.

• For “CRC-8”, a total of 4 altered bits in “FalsePositiveDataMessage” can be obtained by flipping two “Quotient” bits, separated by one unaltered bit.  When only a single bit in “Quotient” is altered, “FalsePositiveDataMessage” will contain 6 error bits.

• There is a short-cut available, which will remove the “Quotient” and “Remainder” from the false-positive procedure:  Use the distribution of “1”s in “Poly” to map alterations in a virtual “Quotient” onto the original “DataMessage”.  It should now be clear that the “Quotient” exists only to demonstrate and complete the fundamental mathematical relations at play in “CRC-Digest” calculation.

The False-Positive Probability:

• The “1-To-1 Function” Concept:  Given a static “Poly”, a unique {“Quotient”, “Remainder”} set will produce 1 unique “DataMessage”, and Vice Versa.

• Further, given the consistent partial set, {“DataMessage”, “Quotient”, “Remainder”}, there exists only 1 unique “Poly”, which completes the set.

• These two mathematical properties of modulo-2 polynomial multiplication-division (transparently reversible) lead to the false-positive probability relation:

• Because any “Quotient” with (length(“DataMessage”) - “W”) bits, can lead to a unique false-positive, 1/(Number of Valid “Remainder”s) is the probability of randomly generating a message, with the right length and a correct “CRC-Digest”.

• The number of “FalsePositiveDataMessage”s is large, but most of them are not plausible, and the number of “NegativeDataMessage”s can be astronomical by comparison.

(Number of Valid “Remainder”s) = (Number of Valid “Poly”s) = 2W

 Poly = |11010101| CalculatedQuotient =|10110100|00001100| TheProduct = |11001010|00101101|11111100| End Event #1 CRC-Digest of TheProduct = |0| - |00000000| (TheProduct XOR CORRECT_CRC_DIGEST) = DataMessage =|11001010|00101101|10100110| End Event #1 The CRC-Digest of the original DataMessage = |90| - |01011010| Poly = |11010101| ModifiedQuotient = |10110100|00001001| TheProduct = |11001010|00101011|01111101| End Event #2 CRC-Digest of TheProduct = |0| - |00000000| (TheProduct XOR CORRECT_CRC_DIGEST) = FalsePositiveDataMessage =|11001010|00101011|00100111| End Event #2 The CRC-Digest of FalsePositiveDataMessage = |90| - |01011010|

 Poly = |11010101| CalculatedQuotient =|10110100|00001100| TheProduct = |11001010|00101101|11111100| End Event #1 CRC-Digest of TheProduct = |0| - |00000000| (TheProduct XOR CORRECT_CRC_DIGEST) = DataMessage =|11001010|00101101|10100110| End Event #1 The CRC-Digest of the original DataMessage = |90| - |01011010| Poly = |11010101| ModifiedQuotient = |11001011|11110011| TheProduct = |10010011|10101101|01001111| End Event #2 CRC-Digest of TheProduct = |0| - |00000000| (TheProduct XOR CORRECT_CRC_DIGEST) = FalsePositiveDataMessage =|10010011|10101101|00010101| End Event #2 The CRC-Digest of FalsePositiveDataMessage = |90| - |01011010|

C Implementation

In the spirit of full disclosure, untampered with versions of the C-Programs and the data-files required to run them are available below.  Any program using 64-bit unsigned long integers needs to be compiled and run with a 64-bit operating system and a 64-bit CPU.  These programs were tested with Ubuntu's GCC compiler.  Of course, to run the program, simply execute the command:  ./a.out

When in Linux, put the ".c" files in your home folder and run this command:  gcc -O3 Clear-CRC-8.c

* -O3:  Level 3 optimization typically adds a major performance boost.

 #include #include #include #define BYTE_WIDTH 8 #define REMAINDER_WIDTH 8 #define MESSAGE_LENGTH 3 // 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 the clearest type of modulo-2 bitwise long-division on "NumberOfBytes" Bytes, starting at "DataMessage". // The divisor used is defined in the constant "Poly". // The value returned by the function is the "FinalRemainder", or CRC-Digest. unsigned char ClearCrc(const unsigned char *DataMessage, unsigned int NumberOfBytes){ unsigned int X; unsigned char CurrentRemainder = 0; unsigned int NextMessageBit; unsigned int NextMessageByte; // Load the first Byte of "DataMessage" into the "CurrentRemainder". memcpy(&CurrentRemainder, DataMessage, sizeof(unsigned char)); NextMessageByte = sizeof(unsigned char); NextMessageBit = (BYTE_WIDTH - 1); // This loop eliminates "1"s in the "MSB" position of "CurrentRemainder" using bit-shifting and the "XOR" operation with "Poly". while ( TRUE ) { // This loop left-shifts "0"s out of the "CurrentRemainder". // The process continues until a "1" is found, or the final message bit is pulled in from the right-hand-side. while ( !(CurrentRemainder & PowersOfTwo[REMAINDER_WIDTH - 1]) ) { CurrentRemainder <<= 1; CurrentRemainder += (DataMessage[NextMessageByte] & PowersOfTwo[NextMessageBit])? 1: 0; // Increment the "NextMessageByte" when we have just pulled bit zero into the "CurrentRemainder". // Exit when there are no more bytes left to process. if ( !NextMessageBit ) { NextMessageByte +=1; // End Event #1. if ( NextMessageByte == NumberOfBytes ) { printf("End Event #1\n"); return CurrentRemainder; } NextMessageBit = (BYTE_WIDTH - 1); } else NextMessageBit -= 1; } CurrentRemainder <<= 1; CurrentRemainder += (DataMessage[NextMessageByte] & PowersOfTwo[NextMessageBit])? 1: 0; // The pivotal "XOR" operation. CurrentRemainder ^= Poly; if ( !NextMessageBit ) { NextMessageByte +=1; // End Event #2 if ( NextMessageByte == NumberOfBytes ) { printf("End Event #2\n"); return CurrentRemainder; } NextMessageBit = (BYTE_WIDTH - 1); } else NextMessageBit -= 1; } } // 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; 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 = ClearCrc(DemonstrationMessage, MESSAGE_LENGTH*sizeof(unsigned char)); 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 = ClearCrc(DemonstrationMessage, MESSAGE_LENGTH*sizeof(unsigned char) + 1); 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 = ClearCrc(DemonstrationMessage, MESSAGE_LENGTH*sizeof(unsigned char) + 1); printf("Behold TheDigest-|%d|-|", TheDigest); PrintByteInBinary(TheDigest); printf("|\n"); printf("\n---------------------------------------------------------------------------------\n"); printf("\n"); return 0; }

 #include #include #include #define BYTE_WIDTH 8 #define TWO_UP_EIGHT 256 // Store the computed table values in a data file to be used in the Bytewise CRC calculation program. #define OUTPUT_FILE "CRC-8.dat" const unsigned char Poly = 213; const unsigned char MSB = 128; const unsigned char PowersOfTwo[BYTE_WIDTH] = { 1, 2, 4, 8, 16, 32, 64, 128 }; // Feed this function "TableIndex" and it will return the corresponding Lookup-Value for CRC-8. unsigned char GenerateLookupTableValue(unsigned char TableIndex){ unsigned int X; unsigned char TheRegister = TableIndex; // Shift a Byte worth of zeros into the register, and when a "1" is shifted out, perform the required "XOR" operation with "Poly". for ( X = BYTE_WIDTH; X; X-- ) { if ( MSB & TheRegister ) TheRegister = (TheRegister << 1) ^ Poly; else TheRegister <<= 1; } return TheRegister; } // 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(){ int X; int Y; unsigned char CalculatedValues[TWO_UP_EIGHT]; FILE* DataOut; DataOut = fopen(OUTPUT_FILE, "wb"); for ( X = 0; X < TWO_UP_EIGHT; X++ ) { CalculatedValues[X] = GenerateLookupTableValue((unsigned char)X); printf("|%3u| - Value-|%2X| - Binary|", X, CalculatedValues[X]); PrintByteInBinary(CalculatedValues[X]); printf("|\n"); } // Verify that each value in the lookup table is unique. // Each value can be the seen as the remainder when a 2-Byte message is divided by "Poly". // The second Byte is always "W" zeros, so only the first byte changes, making it impossible for two remainders to be the same. for ( X = 1; X < TWO_UP_EIGHT; X++ ) { for ( Y = (X - 1); Y >= 0; Y-- ) { if ( CalculatedValues[X] == CalculatedValues[Y] ) printf("Duplicate values found at |%d|, and |%d|\n", X, Y); } } printf("The search for duplicate values in the table is complete.\n"); fwrite(CalculatedValues, sizeof(unsigned char), TWO_UP_EIGHT, DataOut); fclose(DataOut); return 0; }

 #include #include #include #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; }

 #include #include #include #define BYTE_WIDTH 8 #define POLY_WIDTH 64 #define TWO_UP_EIGHT 256 // Store the computed table values in a data file to be used in the Bytewise CRC calculation program. #define OUTPUT_FILE "CRC-64.dat" // "Poly" is the binary representation of the CRC-64 polynomial listed below. // x64 + x62 + x57 + x55 + x54 + x53 + x52 + x47 + x46 + x45 + x40 + x39 + x38 + x37 + x35 + x33 + x32 + x31 + x29 + x27 + x24 + x23 + x22 + x21 + x19 + x17 + x13 + x12 + x10 + x9 + x7 + x4 + x + 1 const unsigned long int Poly = 4823603603198064275; const unsigned long int MSB = 9223372036854775808; const unsigned long int PowersOfTwo[POLY_WIDTH] = {1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536, 131072, 262144, 524288, 1048576, 2097152, 4194304, 8388608, 16777216, 33554432, 67108864, 134217728, 268435456, 536870912, 1073741824, 2147483648, 4294967296, 8589934592, 17179869184, 34359738368, 68719476736, 137438953472, 274877906944, 549755813888, 1099511627776, 2199023255552, 4398046511104, 8796093022208, 17592186044416, 35184372088832, 70368744177664, 140737488355328, 281474976710656, 562949953421312, 1125899906842624, 2251799813685248, 4503599627370496, 9007199254740992, 18014398509481984, 36028797018963968, 72057594037927936, 144115188075855872, 288230376151711744, 576460752303423488, 1152921504606846976, 2305843009213693952, 4611686018427387904, 9223372036854775808}; // Simply print out "ThisLong" using "1"s and "0"s. void PrintUnsignedLongInBinary(unsigned long int ThisLong){ unsigned int X; char HoldOut[POLY_WIDTH + 1]; for ( X = 0; X < POLY_WIDTH; X++ ) HoldOut[X] = (ThisLong & PowersOfTwo[POLY_WIDTH - 1 - X])? '1': '0'; HoldOut[POLY_WIDTH] = '\0'; printf("%s", HoldOut); } // Feed this function "TableIndex" and it will return the corresponding Lookup-Value for CRC-64. unsigned long int GenerateLookupTableValue(unsigned long int TableIndex){ unsigned int X; unsigned long int TheRegister = TableIndex<<56; // Shift a Byte worth of zeros into the register, and when a "1" is shifted out, perform the required "XOR" operation with "Poly". for ( X = BYTE_WIDTH; X; X-- ) { if ( TheRegister & MSB ) TheRegister = (TheRegister << 1) ^ Poly; else TheRegister <<= 1; } return TheRegister; } int main(){ unsigned long int X; int Y; unsigned long int CalculatedValues[TWO_UP_EIGHT]; FILE* DataOut; DataOut = fopen(OUTPUT_FILE, "wb"); for ( X = 0; X < TWO_UP_EIGHT; X++ ) { CalculatedValues[X] = GenerateLookupTableValue(X); printf("|%3lu| - Value-|%16lX| - Binary|", X, CalculatedValues[X]); PrintUnsignedLongInBinary(CalculatedValues[X]); printf("|\n"); } // Verify that each value in the lookup table is unique. // Each value can be the seen as the remainder when a 9-Byte message is divided by "Poly". // The second-to-ninth Bytes are always "W" zeros, so only the first byte changes, making it impossible for two remainders to be the same. for ( X = 1; X < TWO_UP_EIGHT; X++ ) { for ( Y = (X - 1); Y >= 0; Y-- ) { if ( CalculatedValues[X] == CalculatedValues[Y] ) printf("Duplicate values found at |%lu|, and |%d|\n", X, Y); } } printf("The search for duplicate values in the table is complete.\n"); fwrite(CalculatedValues, sizeof(unsigned long int), TWO_UP_EIGHT, DataOut); fclose(DataOut); return 0; }

 #include #include #include // CRC Constants. #define BYTE_WIDTH 8 #define POLY_WIDTH 64 #define TWO_UP_EIGHT 256 #define LEFT_BYTE_SHIFT 56 #define W_BYTES 8 // The pre-compiled CRC-64 lookup-table is stored in this file. #define LOOKUP_TABLE_FILE "CRC-64.dat" // 17x17 4-Colouring Constants. #define POSITION_COUNT 289 #define CHAR_COUNT 73 #define COLOURS 4 #define POSITIONS_PER_NODE 4 // These values are used to decode unsigned long ints into binary format for output. const unsigned long int PowersOfTwo[POLY_WIDTH] = {1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536, 131072, 262144, 524288, 1048576, 2097152, 4194304, 8388608, 16777216, 33554432, 67108864, 134217728, 268435456, 536870912, 1073741824, 2147483648, 4294967296, 8589934592, 17179869184, 34359738368, 68719476736, 137438953472, 274877906944, 549755813888, 1099511627776, 2199023255552, 4398046511104, 8796093022208, 17592186044416, 35184372088832, 70368744177664, 140737488355328, 281474976710656, 562949953421312, 1125899906842624, 2251799813685248, 4503599627370496, 9007199254740992, 18014398509481984, 36028797018963968, 72057594037927936, 144115188075855872, 288230376151711744, 576460752303423488, 1152921504606846976, 2305843009213693952, 4611686018427387904, 9223372036854775808}; // This value is integrated into "CrcLookupTable", but it will not be needed explicitly. const unsigned long int Poly = 4823603603198064275; // The CRC-64 lookup-table will have global scope. unsigned long int CrcLookupTable[TWO_UP_EIGHT]; const unsigned int Modulus[POSITIONS_PER_NODE + 1] = { 0, 1, 2, 3, 0 }; const char TheColours[COLOURS] = { '0', '1', '2', '3' }; const unsigned int PositionShift[POSITIONS_PER_NODE] = { 6, 4, 2, 0 }; // This function uses "NumberOfBytes" Bytes starting at "DataMessage" to compute the CRC-Digest, by way of a Byte-wise "CrcLookupTable" based on the CRC-64 "Poly". unsigned long int LookupTableColouringCrc(const unsigned char *DataMessage, unsigned long int NumberOfBytes){ int X; // Because looking up a "0" in the table returns "0", it is safe to use table-lookups for filling the "WorkingRegister" with its initial "DataMessage" values. register unsigned long int WorkingRegister = 0; // Query the "CrcLookupTable" exactly "NumberOfBytes" times. Perform lookups using the leftmost Byte of "WorkingRegister" as the index. // After each table query, "XOR" the value returned by "CrcLookupTable" with the value inside "WorkingRegister" as soon as the next "DataMessage" Byte has been drawn in from the right-hand-side. // "X" represents the location of the next "DataMessage" Byte to pull into the calculation. for ( X = 0; X < NumberOfBytes; X++ ) WorkingRegister = CrcLookupTable[WorkingRegister >> LEFT_BYTE_SHIFT] ^ ((WorkingRegister << BYTE_WIDTH) ^ DataMessage[X]); return WorkingRegister; } // Read the "POSITION_COUNT" printable characters from "ThisImport" Colouring, and compress them into the "CHAR_COUNT" characters of "ThisColouringString". Compression has a factor of 4. void ImportMasterColouringSeedString(unsigned char *ThisColouringString, const char *ThisImport){ unsigned int X; unsigned int CurrentPosition = 3; // Zero the destination Bytes. memset(ThisColouringString, 0, sizeof(unsigned char)*CHAR_COUNT); // Place the colour at position "X" of "ThisImport", into the correct two-bit slot within "ThisColouringString". Position shifting and modulus lookup-tables are used to speed this up. for ( X = 0; X < POSITION_COUNT; X++ ) ThisColouringString[X >> 2] += ((ThisImport[X] - TheColours) << PositionShift[CurrentPosition = Modulus[CurrentPosition + 1]]); } // Simply print out "ThisLong" using "1"s and "0"s. void PrintUnsignedLongInBinary(unsigned long int ThisLong){ unsigned int X; char HoldOut[POLY_WIDTH + 1]; for ( X = 0; X < POLY_WIDTH; X++ ) HoldOut[X] = (ThisLong & PowersOfTwo[POLY_WIDTH - 1 - X])? '1': '0'; HoldOut[POLY_WIDTH] = '\0'; printf("%s", HoldOut); } // 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(){ int X; unsigned char *CompactString = (unsigned char*)calloc(CHAR_COUNT + W_BYTES, sizeof(unsigned char)); unsigned char *TheFinalW = CompactString + CHAR_COUNT; unsigned long int TheDigest; // These 289-character strings are all different, but it is extremely tedious to find the differences, just by looking at them. Hence we use CRC-64 to help us out. char *FirstImport = "11302023023013131" "20301312322310210" "23123320133011020" "32211100023332301" "00231221230301103" "03022131011312230" "20232030313120111" "31333021100223210" "01103201302122333" "22013233101030102" "33200110302101222" "01220012331233001" "12001332110123023" "30131322001132032" "10020303221021312" "32112103232200013" "13310211020230323\0"; char *SecondImport = "11302023023013131" "20301312322310210" "23123320133011020" "32211100023332301" "00231221230301103" "03022131011312230" "20232030313120111" "31333001100223210" "01103201302122333" "22013233101030102" "33200110302101222" "01220012331233001" "12001332110123023" "30131322001232032" "10020303221021312" "32112103232200013" "13310211020230323\0"; char *ThirdImport = "11302023023013131" "20301312322310210" "23123320133011020" "32211100023332301" "00231221230301103" "03022131011312230" "20232030313120111" "31333021100223210" "01103201302122333" "22013233101030102" "33200110302101222" "01220012331233001" "12001332110123023" "10131322001232032" "10020303221021312" "32112103232200013" "13310211020230323\0"; FILE *LookupTableData; LookupTableData = fopen(LOOKUP_TABLE_FILE, "rb"); fread(CrcLookupTable, sizeof(unsigned long int), TWO_UP_EIGHT, LookupTableData); fclose(LookupTableData); // Process the "FirstImport". printf("\n---------------------------------------------------------------------------------\n\n"); ImportMasterColouringSeedString(CompactString, FirstImport); TheDigest = LookupTableColouringCrc(CompactString, CHAR_COUNT); printf("TheDigest for FirstImport over CHAR_COUNT Bytes -|%lu|\n", TheDigest); TheDigest = LookupTableColouringCrc(CompactString, CHAR_COUNT + W_BYTES); printf("TheDigest for FirstImport plus W zero bits -|%lu|\n", TheDigest); // Copy the (CRC-Digest with W extra zeros) into the extra positions in "CompactString". // It turns out that this is a rather tedious operation, because "unsigned long int"s are stored in "Little Endian Byte Order" in the X86-X64 architecture. // The statement below amounts to reversing the Byte order while copying, so that the most significant Bytes get processed first. for ( X = 0; X < W_BYTES; X++ ) *(TheFinalW + X) = ((unsigned char *)&TheDigest)[W_BYTES - X - 1]; // Verify the the copied Bytes have arrived in the correct order. printf("From TheDigest |"); PrintUnsignedLongInBinary(TheDigest); printf("|\n"); printf("From CompactString|"); for( X = 0; X < W_BYTES; X++ ) { PrintByteInBinary(CompactString[CHAR_COUNT + X]); printf("|"); } printf("\n"); TheDigest = LookupTableColouringCrc(CompactString, CHAR_COUNT + W_BYTES); printf("TheDigest for FirstImport with previous digest appended -|%lu|\n", TheDigest); // Process the "SecondImport". printf("\n---------------------------------------------------------------------------------\n\n"); ImportMasterColouringSeedString(CompactString, SecondImport); TheDigest = LookupTableColouringCrc(CompactString, CHAR_COUNT); printf("TheDigest for SecondImport over CHAR_COUNT Bytes -|%lu|\n", TheDigest); memset(CompactString + CHAR_COUNT, 0, W_BYTES); TheDigest = LookupTableColouringCrc(CompactString, CHAR_COUNT + W_BYTES); printf("TheDigest for SecondImport plus W zero bits -|%lu|\n", TheDigest); // Copy the (CRC-Digest with W extra zeros) into the extra positions in "CompactString". // It turns out that this is a rather tedious operation, because "unsigned long int"s are stored in "Little Endian Byte Order" in the X86-X64 architecture. // The statement below amounts to reversing the Byte order while copying, so that the most significant Bytes get processed first. for ( X = 0; X < W_BYTES; X++ ) *(TheFinalW + X) = ((unsigned char *)&TheDigest)[W_BYTES - X - 1]; // Verify the the copied Bytes have arrived in the correct order. printf("From TheDigest |"); PrintUnsignedLongInBinary(TheDigest); printf("|\n"); printf("From CompactString|"); for( X = 0; X < W_BYTES; X++ ) { PrintByteInBinary(CompactString[CHAR_COUNT + X]); printf("|"); } printf("\n"); TheDigest = LookupTableColouringCrc(CompactString, CHAR_COUNT + W_BYTES); printf("TheDigest for SecondImport with previous digest appended -|%lu|\n", TheDigest); // Process the "ThirdImport". printf("\n---------------------------------------------------------------------------------\n\n"); ImportMasterColouringSeedString(CompactString, ThirdImport); TheDigest = LookupTableColouringCrc(CompactString, CHAR_COUNT); printf("TheDigest for ThirdImport over CHAR_COUNT Bytes -|%lu|\n", TheDigest); memset(CompactString + CHAR_COUNT, 0, W_BYTES); TheDigest = LookupTableColouringCrc(CompactString, CHAR_COUNT + W_BYTES); printf("TheDigest for ThirdImport plus W zero bits -|%lu|\n", TheDigest); // Copy the (CRC-Digest with W extra zeros) into the extra positions in "CompactString". // It turns out that this is a rather tedious operation, because "unsigned long int"s are stored in "Little Endian Byte Order" in the X86-X64 architecture. // The statement below amounts to reversing the Byte order while copying, so that the most significant Bytes get processed first. for ( X = 0; X < W_BYTES; X++ ) *(TheFinalW + X) = ((unsigned char *)&TheDigest)[W_BYTES - X - 1]; // Verify the the copied Bytes have arrived in the correct order. printf("From TheDigest |"); PrintUnsignedLongInBinary(TheDigest); printf("|\n"); printf("From CompactString|"); for( X = 0; X < W_BYTES; X++ ) { PrintByteInBinary(CompactString[CHAR_COUNT + X]); printf("|"); } printf("\n"); TheDigest = LookupTableColouringCrc(CompactString, CHAR_COUNT + W_BYTES); printf("TheDigest for ThirdImport with previous digest appended -|%lu|\n", TheDigest); printf("\n---------------------------------------------------------------------------------\n\n"); return 0; }

 #include #include #include #define BYTE_WIDTH 8 #define REMAINDER_WIDTH 8 #define MESSAGE_LENGTH 3 #define CORRECT_CRC_DIGEST 90 // 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}; const unsigned char Poly = 213; // 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); } // This function mod-2-polynomial-multiplies "TheQuotient" by "Poly" and stores the result into "TheProduct", which will be "MessageBytes" long. void ModTwoPolyProduct(unsigned char *TheQuotient, unsigned char *TheProduct, unsigned int MessageBytes){ unsigned int X; unsigned int Y; unsigned int PowerSum; // First zero the value of "TheProduct". memset(TheProduct, 0, MessageBytes); // X Keeps track of the position within "Poly". for ( X = 0; X <= BYTE_WIDTH; X++ ) { // This condition accounts for the token "1" ahead of "Poly". if ( BYTE_WIDTH - X ) if ( !(PowersOfTwo[X] & Poly) ) continue; // Y Keeps track of the position within "TheQuotient". for ( Y = 0; Y < (BYTE_WIDTH*(MessageBytes - 1)); Y++ ) { // If the Y position in "TheQuotient" is "0", then bounce. if ( !(TheQuotient[(MessageBytes - 1 - 1) - (Y>>3)] & PowersOfTwo[Y%BYTE_WIDTH]) ) continue; PowerSum = X + Y; TheProduct[(MessageBytes - 1) - (PowerSum>>3)] ^= PowersOfTwo[PowerSum%BYTE_WIDTH]; } } } // This function performs the clearest type of modulo-2 bitwise long-division on "NumberOfBytes" Bytes, starting at "DataMessage". // The divisor used is defined in the constant "Poly". // The value returned by the function is the "FinalRemainder", or CRC-Digest. unsigned char ClearCrc(const unsigned char *DataMessage, unsigned int NumberOfBytes){ unsigned int X; unsigned char CurrentRemainder = 0; unsigned int NextMessageBit; unsigned int NextMessageByte; // Load the first Byte of "DataMessage" into the "CurrentRemainder". memcpy(&CurrentRemainder, DataMessage, sizeof(unsigned char)); NextMessageByte = sizeof(unsigned char); NextMessageBit = (BYTE_WIDTH - 1); // This loop eliminates "1"s in the "MSB" position of "CurrentRemainder" using bit-shifting and the "XOR" operation with "Poly". while ( TRUE ) { // This loop left-shifts "0"s out of the "CurrentRemainder". // The process continues until a "1" is found, or the final message bit is pulled in from the right-hand-side. while ( !(CurrentRemainder & PowersOfTwo[REMAINDER_WIDTH - 1]) ) { CurrentRemainder <<= 1; CurrentRemainder += (DataMessage[NextMessageByte] & PowersOfTwo[NextMessageBit])? 1: 0; // Increment the "NextMessageByte" when we have just pulled bit zero into the "CurrentRemainder". // Exit when there are no more bytes left to process. if ( !NextMessageBit ) { NextMessageByte +=1; // End Event #1. if ( NextMessageByte == NumberOfBytes ) { printf("End Event #1\n"); return CurrentRemainder; } NextMessageBit = (BYTE_WIDTH - 1); } else NextMessageBit -= 1; } CurrentRemainder <<= 1; CurrentRemainder += (DataMessage[NextMessageByte] & PowersOfTwo[NextMessageBit])? 1: 0; // The pivotal "XOR" operation. CurrentRemainder ^= Poly; if ( !NextMessageBit ) { NextMessageByte +=1; // End Event #2 if ( NextMessageByte == NumberOfBytes ) { printf("End Event #2\n"); return CurrentRemainder; } NextMessageBit = (BYTE_WIDTH - 1); } else NextMessageBit -= 1; } } int main(){ unsigned int X; unsigned char DataMessage[MESSAGE_LENGTH]; unsigned char FalsePositiveDataMessage[MESSAGE_LENGTH]; unsigned char CalculatedQuotient[MESSAGE_LENGTH - 1] = { 180, 12 }; // In order to keep the "FalsePositiveDataMessage" plausible, the "MSB" of the most significant Byte should be set to "1". // Further, changes made to the quotient should minimize the bits altered in the "DataMessage". This requires knowledge of the "Poly" being used. // For CRC-8, a single bit flip in the "Quotient" results in a six-bit alteration of the "DataMessage". // It is possible to limits the incorrect bits in "FalsePositiveDataMessage" to four by altering 2 bits in "Quotient", seperated by one unchanged bit. // This particular modification of "CalculatedQuotient" results in ten bit-errors. unsigned char ModifiedQuotient[MESSAGE_LENGTH - 1] = { 203, 243 }; unsigned char TheCrcDigest; // Display the chosen "Poly". printf("\nPoly = |"); PrintByteInBinary(Poly); printf("|\n"); printf("CalculatedQuotient =|"); for ( X = 0; X < (MESSAGE_LENGTH - 1); X++ ) { PrintByteInBinary(CalculatedQuotient[X]); printf("|"); } printf("\n"); ModTwoPolyProduct(CalculatedQuotient, DataMessage, MESSAGE_LENGTH); printf("TheProduct = |"); for ( X = 0; X < MESSAGE_LENGTH; X++ ) { PrintByteInBinary(DataMessage[X]); printf("|"); } printf("\n"); TheCrcDigest = ClearCrc(DataMessage, MESSAGE_LENGTH); printf("CRC-Digest of TheProduct = |%u| - |", TheCrcDigest); PrintByteInBinary(TheCrcDigest); printf("|\n"); // XOR the "CORRECT_CRC_DIGEST" at the end of "DataMessage". DataMessage[MESSAGE_LENGTH - 1] ^= CORRECT_CRC_DIGEST; printf("(TheProduct XOR CORRECT_CRC_DIGEST) = DataMessage =|"); for ( X = 0; X < MESSAGE_LENGTH; X++ ) { PrintByteInBinary(DataMessage[X]); printf("|"); } printf("\n"); TheCrcDigest = ClearCrc(DataMessage, MESSAGE_LENGTH); printf("The CRC-Digest of the original DataMessage = |%u| - |", TheCrcDigest); PrintByteInBinary(TheCrcDigest); printf("|\n\n"); // Display the chosen "Poly". printf("\nPoly = |"); PrintByteInBinary(Poly); printf("|\n"); printf("ModifiedQuotient = |"); for ( X = 0; X < (MESSAGE_LENGTH - 1); X++ ) { PrintByteInBinary(ModifiedQuotient[X]); printf("|"); } printf("\n"); ModTwoPolyProduct(ModifiedQuotient, FalsePositiveDataMessage, MESSAGE_LENGTH); printf("TheProduct = |"); for ( X = 0; X < MESSAGE_LENGTH; X++ ) { PrintByteInBinary(FalsePositiveDataMessage[X]); printf("|"); } printf("\n"); TheCrcDigest = ClearCrc(FalsePositiveDataMessage, MESSAGE_LENGTH); printf("CRC-Digest of TheProduct = |%u| - |", TheCrcDigest); PrintByteInBinary(TheCrcDigest); printf("|\n"); // XOR the "CORRECT_CRC_DIGEST" at the end of "FalsePositiveDataMessage". FalsePositiveDataMessage[MESSAGE_LENGTH - 1] ^= CORRECT_CRC_DIGEST; printf("(TheProduct XOR CORRECT_CRC_DIGEST) = FalsePositiveDataMessage =|"); for ( X = 0; X < MESSAGE_LENGTH; X++ ) { PrintByteInBinary(FalsePositiveDataMessage[X]); printf("|"); } printf("\n"); TheCrcDigest = ClearCrc(FalsePositiveDataMessage, MESSAGE_LENGTH); printf("The CRC-Digest of FalsePositiveDataMessage = |%u| - |", TheCrcDigest); PrintByteInBinary(TheCrcDigest); printf("|\n\n"); return 0; }

Conclusion

• The What:  Document the transition between modulo-2 long-division and optimized Byte-wise Lookup-Table CRC-Digest Calculation.
• The How:  Perform several fundamental calculations by hand, and present 6 sleek C-programs that get the job done.
• The Why:  Because those who know, treat it as self-evident, while those who don't, need to read many web pages until they agree.

Below, the input, and output of crc.c will be put on display.  Simply glance at the three input 17x17 4-Colourings; They are quite large, and look identical to the naked eye, but when they each produce a unique “CRC-Digest”, you will be looking for the subtle differences, just to make sure.

The Three Input 17x17 4-Colourings:
 11302023023013131 20301312322310210 23123320133011020 32211100023332301 00231221230301103 03022131011312230 20232030313120111 31333021100223210 01103201302122333 22013233101030102 33200110302101222 01220012331233001 12001332110123023 30131322001132032 10020303221021312 32112103232200013 13310211020230323
 11302023023013131 20301312322310210 23123320133011020 32211100023332301 00231221230301103 03022131011312230 20232030313120111 31333001100223210 01103201302122333 22013233101030102 33200110302101222 01220012331233001 12001332110123023 30131322001232032 10020303221021312 32112103232200013 13310211020230323
 11302023023013131 20301312322310210 23123320133011020 32211100023332301 00231221230301103 03022131011312230 20232030313120111 31333021100223210 01103201302122333 22013233101030102 33200110302101222 01220012331233001 12001332110123023 10131322001232032 10020303221021312 32112103232200013 13310211020230323

 --------------------------------------------------------------------------------- TheDigest for FirstImport over CHAR_COUNT Bytes -|2449492796987567755| TheDigest for FirstImport plus W zero bits -|2693988645103658150| From TheDigest |0010010101100010111110000110011000111100010101101110010010100110| From CompactString|00100101|01100010|11111000|01100110|00111100|01010110|11100100|10100110| TheDigest for FirstImport with previous digest appended -|0| --------------------------------------------------------------------------------- TheDigest for SecondImport over CHAR_COUNT Bytes -|13695663649870972449| TheDigest for SecondImport plus W zero bits -|14420216274443853120| From TheDigest |1100100000011110111001100100110011001110100010000101110101000000| From CompactString|11001000|00011110|11100110|01001100|11001110|10001000|01011101|01000000| TheDigest for SecondImport with previous digest appended -|0| --------------------------------------------------------------------------------- TheDigest for ThirdImport over CHAR_COUNT Bytes -|13202989161120760022| TheDigest for ThirdImport plus W zero bits -|4279025832619793549| From TheDigest |0011101101100010001001110101001011010010000001011101110010001101| From CompactString|00111011|01100010|00100111|01010010|11010010|00000101|11011100|10001101| TheDigest for ThirdImport with previous digest appended -|0| ---------------------------------------------------------------------------------

Contact Information

JohnPaul Adamovsky 