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