BinaryMath.php 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  1. <?php
  2. namespace IPLib\Service;
  3. /**
  4. * Helper class to work with unsigned binary integers.
  5. */
  6. class BinaryMath
  7. {
  8. /**
  9. * Trim the leading zeroes from a non-negative integer represented in binary form.
  10. *
  11. * @param string $value
  12. *
  13. * @return string
  14. */
  15. public function reduce($value)
  16. {
  17. $value = ltrim($value, '0');
  18. return $value === '' ? '0' : $value;
  19. }
  20. /**
  21. * Compare two non-negative integers represented in binary form.
  22. *
  23. * @param string $a
  24. * @param string $b
  25. *
  26. * @return int 1 if $a is greater than $b, -1 if $b is greater than $b, 0 if they are the same
  27. */
  28. public function compare($a, $b)
  29. {
  30. list($a, $b) = $this->toSameLength($a, $b);
  31. return $a < $b ? -1 : ($a > $b ? 1 : 0);
  32. }
  33. /**
  34. * Add 1 to a non-negative integer represented in binary form.
  35. *
  36. * @param string $value
  37. *
  38. * @return string
  39. */
  40. public function increment($value)
  41. {
  42. $lastZeroIndex = strrpos($value, '0');
  43. if ($lastZeroIndex === false) {
  44. return '1' . str_repeat('0', strlen($value));
  45. }
  46. return ltrim(substr($value, 0, $lastZeroIndex), '0') . '1' . str_repeat('0', strlen($value) - $lastZeroIndex - 1);
  47. }
  48. /**
  49. * Calculate the bitwise AND of two non-negative integers represented in binary form.
  50. *
  51. * @param string $operand1
  52. * @param string $operand2
  53. *
  54. * @return string
  55. */
  56. public function andX($operand1, $operand2)
  57. {
  58. $operand1 = $this->reduce($operand1);
  59. $operand2 = $this->reduce($operand2);
  60. $numBits = min(strlen($operand1), strlen($operand2));
  61. $operand1 = substr(str_pad($operand1, $numBits, '0', STR_PAD_LEFT), -$numBits);
  62. $operand2 = substr(str_pad($operand2, $numBits, '0', STR_PAD_LEFT), -$numBits);
  63. $result = '';
  64. for ($index = 0; $index < $numBits; $index++) {
  65. $result .= $operand1[$index] === '1' && $operand2[$index] === '1' ? '1' : '0';
  66. }
  67. return $this->reduce($result);
  68. }
  69. /**
  70. * Calculate the bitwise OR of two non-negative integers represented in binary form.
  71. *
  72. * @param string $operand1
  73. * @param string $operand2
  74. *
  75. * @return string
  76. */
  77. public function orX($operand1, $operand2)
  78. {
  79. list($operand1, $operand2, $numBits) = $this->toSameLength($operand1, $operand2);
  80. $result = '';
  81. for ($index = 0; $index < $numBits; $index++) {
  82. $result .= $operand1[$index] === '1' || $operand2[$index] === '1' ? '1' : '0';
  83. }
  84. return $result;
  85. }
  86. /**
  87. * Zero-padding of two non-negative integers represented in binary form, so that they have the same length.
  88. *
  89. * @param string $num1
  90. * @param string $num2
  91. *
  92. * @return string[],int[] The first array element is $num1 (padded), the first array element is $num2 (padded), the third array element is the number of bits
  93. */
  94. private function toSameLength($num1, $num2)
  95. {
  96. $num1 = $this->reduce($num1);
  97. $num2 = $this->reduce($num2);
  98. $numBits = max(strlen($num1), strlen($num2));
  99. return array(
  100. str_pad($num1, $numBits, '0', STR_PAD_LEFT),
  101. str_pad($num2, $numBits, '0', STR_PAD_LEFT),
  102. $numBits,
  103. );
  104. }
  105. }