BinaryMath.php 3.3 KB

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