/** * Compute the parity of a number using a technique to erase the LSB. * * i.e. * 64 = 1000000 * 1000000 / result = 1 * parity of 64 is 1 * This works because 64 is &'d with 63 = 1000000 is &'d with 0111111. As we can see that operation will return 0 * which evaluates to false so the while loop terminates. * * 65 = 1000001 * 1000001 / result = 1 * 1000000 / result = 0 * parity of 65 is 0 * * This improves performance in the best and average cases but can still be as slow as brute force. For example if run * on 9223372036854775807 it will take 63 iterations which matches brute force. * * @param int $number * @return int * @throws \InvalidArgumentException */ function computeParityEraseLowestSetBit($number) { if (!is_int($number)) { throw new \InvalidArgumentException('$number must be an integer'); } $bitwiseHelper = new BitwiseHelper(); $number = $bitwiseHelper->eraseSignBit($number); $result = 0; while ($number) { $result ^= 1; $number &= $number - 1; } return $result; }
/** * Get an array of the pre-computed reverse of all 8-bit words * * @return array */ function getReverseOf8BitWords() { static $cachedReverses = []; if (empty($cachedReverses)) { if (!file_exists('computed_reverses.txt')) { $bitwiseHelper = new BitwiseHelper(); $cachedReverses = $bitwiseHelper->computeReverseOf8BitWords(0b11111111); $fp = fopen('computed_reverses.txt', 'w'); fwrite($fp, serialize($cachedReverses)); fclose($fp); } else { $cachedReverses = unserialize(file_get_contents('computed_reverses.txt')); } } return $cachedReverses; }
/** * Compute the parity of a number by comparing the number to smaller halves of itself over and over. Because XOR is * commutative we can do this until we are down to 4 bits and then use a pre-computed lookup table to compute the final * parity. * i.e. * 64 = 0000000000000000000000000000000000000000000000000000000001000000 * * 0000000000000000000000000000000000000000000000000000000001000000 * ^ 00000000000000000000000000000000 / Shift 32 bits * -------------------------------------------------------------------- * 0000000000000000000000000000000000000000000000000000000001000000 * ^ 000000000000000000000000000000000000000000000000 / Shift 16 bits * -------------------------------------------------------------------- * 0000000000000000000000000000000000000000000000000000000001000000 * ^ 00000000000000000000000000000000000000000000000000000000 / Shift 8 bits * -------------------------------------------------------------------- * 0000000000000000000000000000000000000000000000000000000001000000 * ^ 000000000000000000000000000000000000000000000000000000000100 / Shift 4 bits * -------------------------------------------------------------------- * 0000000000000000000000000000000000000000000000000000000001000100 * 0100 / Last 4 bits * 0110100110010110 >> 4 & 1 = 011010011001 & 1 = 1 / Parity * * 65 = 0000000000000000000000000000000000000000000000000000000001000001 * * 0000000000000000000000000000000000000000000000000000000001000001 * ^ 00000000000000000000000000000000 / Shift 32 bits * -------------------------------------------------------------------- * 0000000000000000000000000000000000000000000000000000000001000001 * ^ 000000000000000000000000000000000000000000000000 / Shift 16 bits * -------------------------------------------------------------------- * 0000000000000000000000000000000000000000000000000000000001000001 * ^ 00000000000000000000000000000000000000000000000000000000 / Shift 8 bits * -------------------------------------------------------------------- * 0000000000000000000000000000000000000000000000000000000001000001 * ^ 000000000000000000000000000000000000000000000000000000000100 / Shift 4 bits * -------------------------------------------------------------------- * 0000000000000000000000000000000000000000000000000000000001000101 * 0101 / Last 4 bits * 0110100110010110 >> 5 & 1 = 01101001100 & 1 = 0 / Parity * * @param $number * @return int * @throws \InvalidArgumentException */ function computeParityShifting($number) { if (!is_int($number)) { throw new \InvalidArgumentException('$number must be an integer'); } $bitwiseHelper = new BitwiseHelper(); $number = $bitwiseHelper->eraseSignBit($number); // This contains the parity of 0, 1, 2 ... starting from the LSB static $fourBitLookupTable = 0b110100110010110; if (PHP_INT_SIZE == 8) { $number ^= $number >> BitwiseHelper::WORD_SIZE * 4; } $number ^= $number >> BitwiseHelper::WORD_SIZE * 2; $number ^= $number >> BitwiseHelper::WORD_SIZE; $number ^= $number >> BitwiseHelper::WORD_SIZE / 2; $number &= 0b1111; return $fourBitLookupTable >> $number & 1; }