Each new bit updates the remainder
· algorithms
Bits already cancelled never return. The remainder of the next prefix is rem_G(x R_k + b_{k+1}), and r trailing zeros make that the CRC.
Last time I divided the whole shifted message at once. A high bit, once cancelled, never comes back. The past of the dividend compresses into the remainder I already have, and the next bit is the only new information.
The next prefix
Read the message from the high bit down, bits . The prefixes of are
110101101110110101101Let be the polynomial of the first bits, and let . Appending the next bit shifts that prefix left and writes in the empty place:
If , then
so
I never need again. After bits the remainder has degree less than , which is bits of state. The loop starts at , the empty prefix.
The direct translation shifts the remainder and then calls the long division from last time:
remainder = (remainder << 1) | static_cast<uint32_t>(bit);remainder = polynomial_remainder(remainder, generator);remainder << 1 with bit, that is , in the low place is . The call is of that polynomial.
The same message
Take , so , and the message . The running remainder stays under degree for the first four bits: , , , . The fifth bit shifts to , and the degree reaches :
10110^ 10011------- 00101The sixth bit shifts to . The degree is , so nothing is cancelled, and
That is , not the CRC. The CRC is , which is this same walk continued through four zero bits, the room at the end of the message. From :
10110^ 10011------- 00101then , then
10100^ 10011------- 00111then . The remainder is , the same CRC as the one-shot division.
streaming_crc appends those width zeros and hands the whole string to streaming_remainder.
One reduction is enough
After bits, , so
The shifted remainder has at most one bit above . If that bit is , the degree is already less than . If it is , a single XOR with cancels it, because both leading coefficients are . The long-division loop would run at most once. Bit width is that leading coefficient:
[[nodiscard]] uint32_t streaming_remainder( const std::vector<int>& dividend_bits, uint32_t divisor) { uint32_t remainder = 0; const int width = polynomial_degree(divisor);
for (const int bit : dividend_bits) { remainder = (remainder << 1) | static_cast<uint32_t>(bit); if ((remainder & (1LL << width)) > 0) { remainder = remainder ^ divisor; } }
return remainder;}
[[nodiscard]] uint32_t streaming_crc(const std::vector<int>& message_bits, uint32_t generator) { const int width = polynomial_degree(generator); std::vector<int> augmented = message_bits; augmented.resize(augmented.size() + static_cast<size_t>(width), 0); return streaming_remainder(augmented, generator);}The generator’s leading is there to cancel the bit that just reached degree . The next step is a register that does not store that bit.