/** * 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; }
/** * Compute the parity of a number by breaking it into smaller words and xor'ing the parity of each word. The first * step is to cache the parity of all words and then we can use the cached values to speed up the check of the * number we want to test. * i.e. * 64 = 1000000 which is * parity[00000000] ^ parity[00000000] ^ parity[00000000] ^ parity[00000000] ^ parity[00000000] ^ parity[00000000] ^ parity[01000000] or * 0 ^ 0 ^ 0 ^ 0 ^ 0 ^ 0 ^ 0 ^ 1 = 1 * * @param $number * @return int * @throws \InvalidArgumentException */ function computeParityCache($number) { if (!is_int($number)) { throw new \InvalidArgumentException('$number must be an integer'); } $bitwiseHelper = new BitwiseHelper(); $number = $bitwiseHelper->eraseSignBit($number); $precomputedParities = getParityOfWords(); $bitmask = 0b11111111; // 32-bit implementation if (PHP_INT_SIZE == 4) { return $precomputedParities[$number >> 3 * BitwiseHelper::WORD_SIZE & $bitmask] ^ $precomputedParities[$number >> 2 * BitwiseHelper::WORD_SIZE & $bitmask] ^ $precomputedParities[$number >> BitwiseHelper::WORD_SIZE & $bitmask] ^ $precomputedParities[$number & $bitmask]; } // 64-bit implementation return $precomputedParities[$number >> 7 * BitwiseHelper::WORD_SIZE & $bitmask] ^ $precomputedParities[$number >> 6 * BitwiseHelper::WORD_SIZE & $bitmask] ^ $precomputedParities[$number >> 5 * BitwiseHelper::WORD_SIZE & $bitmask] ^ $precomputedParities[$number >> 4 * BitwiseHelper::WORD_SIZE & $bitmask] ^ $precomputedParities[$number >> 3 * BitwiseHelper::WORD_SIZE & $bitmask] ^ $precomputedParities[$number >> 2 * BitwiseHelper::WORD_SIZE & $bitmask] ^ $precomputedParities[$number >> BitwiseHelper::WORD_SIZE & $bitmask] ^ $precomputedParities[$number & $bitmask]; }
/** * Reverse the bits in a number. This works by first caching the values of all 8-bit words with their bits reversed. * Then, using the cached values each 8-bit segment of the number is used to build the number with all bits reversed. * The least significant 8-bits of the number are reversed and used as the most significant 8-bits. Then the next * least significant and so on. * * @param int $number * @return int * @throws \InvalidArgumentException */ function reverseBits($number) { if (!is_int($number)) { throw new \InvalidArgumentException('$number must be an integer'); } $bitwiseHelper = new BitwiseHelper(); $number = $bitwiseHelper->eraseSignBit($number); $precomputedReverses = getReverseOf8BitWords(); $bitmask = 0b11111111; // 32-bit implementation if (PHP_INT_SIZE == 4) { return $precomputedReverses[$number & $bitmask] << 3 * BitwiseHelper::WORD_SIZE | $precomputedReverses[$number >> BitwiseHelper::WORD_SIZE & $bitmask] << 2 * BitwiseHelper::WORD_SIZE | $precomputedReverses[$number >> 2 * BitwiseHelper::WORD_SIZE & $bitmask] << BitwiseHelper::WORD_SIZE | $precomputedReverses[$number >> 3 * BitwiseHelper::WORD_SIZE & $bitmask]; } // 64-bit implementation return $precomputedReverses[$number & $bitmask] << 7 * BitwiseHelper::WORD_SIZE | $precomputedReverses[$number >> BitwiseHelper::WORD_SIZE & $bitmask] << 6 * BitwiseHelper::WORD_SIZE | $precomputedReverses[$number >> 2 * BitwiseHelper::WORD_SIZE & $bitmask] << 5 * BitwiseHelper::WORD_SIZE | $precomputedReverses[$number >> 3 * BitwiseHelper::WORD_SIZE & $bitmask] << 4 * BitwiseHelper::WORD_SIZE | $precomputedReverses[$number >> 4 * BitwiseHelper::WORD_SIZE & $bitmask] << 3 * BitwiseHelper::WORD_SIZE | $precomputedReverses[$number >> 5 * BitwiseHelper::WORD_SIZE & $bitmask] << 2 * BitwiseHelper::WORD_SIZE | $precomputedReverses[$number >> 6 * BitwiseHelper::WORD_SIZE & $bitmask] << BitwiseHelper::WORD_SIZE | $precomputedReverses[$number >> 7 * BitwiseHelper::WORD_SIZE & $bitmask]; }
/** * 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; }