// File: /workspace/github_c_makefile_star2_O2/dlbeer[P]dhara/ecc/hamming.c
// Function: parity_scan
static hamming_ecc_t parity_scan(const uint8_t *chunk, size_t len)
{
	uint8_t column = 0;
	uint16_t line = 0;
	uint16_t line_bar = 0;
	hamming_ecc_t out = 0;
	int i;

	/* Let the bits in chunk be partitioned into the following
	 * subsets:
	 *
	 *    P0 : bits  0,  2,  4,  6,  8, 10, 12, 14...
	 *    P0': bits  1,  3,  5,  7,  9, 11, 13, 15...
	 *    P1 : bits  0,  1,  4,  5,  8,  9, 12, 13...
	 *    P1': bits  2,  3,  6,  7, 10, 11, 14, 15...
	 *    P2 : bits  0,  1,  2,  3,  8,  9, 10, 11...
	 *    P2': bits  4,  5,  6,  7, 12, 13, 14, 15...
         *    P3 : bits  0,  1,  2,  3,  4,  5,  6,  7...
	 *    P3': bits  8,  9, 10, 11, 12, 13, 14, 15...
	 *
	 * That is, given a bit position i, and a pair of sets (Pm,
	 * Pm'), then i belongs to Pm if bit m in i is clear. Otherwise,
	 * it belongs to Pm'.
	 */
	for (i = 0; i < len; i++) {
		const uint8_t c = chunk[i];

		column ^= c;

		if (bit_count[c] & 1) {
			line ^= i;
			line_bar ^= ~i;
		}
	}

	/* The output checksum is the parity of the sets, in the
	 * following order:
	 *
	 *     ...P3', P3, P2', P2, P1', P1, P0', P0
	 *
	 * This is a linear code: the parity of the difference of two
	 * blocks is equal to the difference of their parities.
	 *
	 * If the bit at position i is flipped, then it flips the parity
	 * of exactly one of each pair of sets (position i belongs to
	 * one of each pair of sets).
	 *
	 * By observing which of each pair has changed parity, we can
	 * determine each bit of i.
	 */
	for (i = 0; i < HAMMING_LOG2_CHUNK_SIZE; i++) {
		out <<= 1;
		out |= (line_bar >> 8) & 1;

		out <<= 1;
		out |= (line >> 8) & 1;

		line <<= 1;
		line_bar <<= 1;
	}

	out = (out << 1) | (bit_count[column & 0x0f] & 1);
	out = (out << 1) | (bit_count[column & 0xf0] & 1);
	out = (out << 1) | (bit_count[column & 0x33] & 1);
	out = (out << 1) | (bit_count[column & 0xcc] & 1);
	out = (out << 1) | (bit_count[column & 0x55] & 1);
	out = (out << 1) | (bit_count[column & 0xaa] & 1);

	return out ^ 0xffffff;
}

// Assembly:
pushq %rbx
xorl %r9d, %r9d
leaq 0xe63(%rip), %rax       # 0x2010
xorl %r10d, %r10d
xorl %ecx, %ecx
xorl %r8d, %r8d
xorl %edx, %edx
cmpq %r10, %rsi
je 0x11e7
movzbl (%rdi,%r10), %r11d
xorb %r11b, %dl
movl %r8d, %ebx
xorl %r10d, %ebx
testb $0x1, (%r11,%rax)
notl %ebx
movl %r10d, %r11d
cmovel %r9d, %r11d
cmovel %r8d, %ebx
xorl %r11d, %ecx
incq %r10
movl %ebx, %r8d
jmp 0x11b7
xorl %esi, %esi
movl $0x9, %edi
subl $0x1, %edi
jb 0x1214
movl %r8d, %r9d
shrl $0x7, %r9d
andl $0x2, %r9d
leal (%r9,%rsi,4), %r9d
movl %ecx, %esi
shrl $0x8, %esi
andl $0x1, %esi
orl %r9d, %esi
addl %ecx, %ecx
addl %r8d, %r8d
jmp 0x11ee
movzbl %dl, %ecx
movl %ecx, %edx
andl $0xf, %edx
movzbl (%rdx,%rax), %edx
andl $0x1, %edx
addl %edx, %edx
leal (%rdx,%rsi,4), %edx
movl %ecx, %esi
andl $-0x10, %esi
movzbl (%rsi,%rax), %esi
andl $0x1, %esi
orl %edx, %esi
movl %ecx, %edx
andl $0x33, %edx
movzbl (%rdx,%rax), %edx
andl $0x1, %edx
addl %edx, %edx
leal (%rdx,%rsi,4), %edx
movl %ecx, %esi
andl $-0x34, %esi
movzbl (%rsi,%rax), %esi
andl $0x1, %esi
orl %edx, %esi
movl %ecx, %edx
andl $0x55, %edx
movzbl (%rdx,%rax), %edx
andl $0x1, %edx
addl %edx, %edx
leal (%rdx,%rsi,4), %edx
andl $-0x56, %ecx
movzbl (%rcx,%rax), %eax
andl $0x1, %eax
orl %edx, %eax
xorl $0xffffff, %eax         # imm = 0xFFFFFF
popq %rbx
retq